birth
This commit is contained in:
93
detection/README.md
Normal file
93
detection/README.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# FlashInternImage for Object Detection
|
||||
|
||||
This folder contains the implementation of the FlashInternImage for object detection.
|
||||
|
||||
Our detection code is developed on top of [MMDetection v2.28.1](https://github.com/open-mmlab/mmdetection/tree/v2.28.1).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Install
|
||||
|
||||
- Clone this repo:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/OpenGVLab/DCNv4.git
|
||||
cd DCNv4
|
||||
```
|
||||
|
||||
- Create a conda virtual environment and activate it:
|
||||
|
||||
```bash
|
||||
conda create -n dcnv4 python=3.7 -y
|
||||
conda activate dcnv4
|
||||
```
|
||||
|
||||
- Install `CUDA>=10.2` with `cudnn>=7` following
|
||||
the [official installation instructions](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html)
|
||||
- Install `PyTorch>=1.10.0` and `torchvision>=0.9.0` with `CUDA>=10.2`:
|
||||
|
||||
For examples, to install torch==1.11 with CUDA==11.3:
|
||||
```bash
|
||||
pip install torch==1.11.0+cu113 torchvision==0.12.0+cu113 -f https://download.pytorch.org/whl/torch_stable.html
|
||||
```
|
||||
|
||||
- Install `timm==0.6.11` and `mmcv-full==1.5.0`:
|
||||
|
||||
```bash
|
||||
pip install -U openmim
|
||||
mim install mmcv-full==1.5.0
|
||||
pip install timm==0.6.11 mmdet==2.28.1
|
||||
```
|
||||
|
||||
- Install other requirements:
|
||||
|
||||
```bash
|
||||
pip install opencv-python termcolor yacs pyyaml scipy
|
||||
```
|
||||
|
||||
- Install DCNv4
|
||||
```bash
|
||||
pip install DCNv4==latest
|
||||
```
|
||||
|
||||
|
||||
### Data Preparation
|
||||
|
||||
Prepare COCO according to the guidelines in [MMDetection v2.28.1](https://github.com/open-mmlab/mmdetection/resolve/master/docs/en/1_exist_data_model.md).
|
||||
|
||||
|
||||
### Evaluation
|
||||
|
||||
To evaluate our `FlashInternImage` on COCO val, run:
|
||||
|
||||
```bash
|
||||
sh dist_test.sh <config-file> <checkpoint> <gpu-num> --eval bbox segm
|
||||
```
|
||||
|
||||
For example, to evaluate the `FlashInternImage-T` with a single GPU:
|
||||
|
||||
```bash
|
||||
python test.py configs/coco/mask_rcnn_flash_intern_image_t_fpn_1x_coco.py checkpoint_dir/det/mask_rcnn_flash_internimage_t_fpn_1x_coco.pth --eval bbox segm
|
||||
```
|
||||
|
||||
For example, to evaluate the `FlashInternImage-B` with a single node with 8 GPUs:
|
||||
|
||||
```bash
|
||||
sh dist_test.sh configs/coco/mask_rcnn_flash_intern_image_b_fpn_1x_coco.py checkpoint_dir/det/mask_rcnn_flash_internimage_b_fpn_1x_coco.py 8 --eval bbox segm
|
||||
```
|
||||
|
||||
### Training on COCO
|
||||
|
||||
To train an `FlashInternImage` on COCO, run:
|
||||
|
||||
```bash
|
||||
sh dist_train.sh <config-file> <gpu-num>
|
||||
```
|
||||
|
||||
For example, to train `FlashInternImage-T` with 8 GPU on 1 node, run:
|
||||
|
||||
```bash
|
||||
sh dist_train.sh configs/coco/mask_rcnn_flash_intern_image_t_fpn_1x_coco.py 8
|
||||
```
|
||||
|
||||
49
detection/configs/_base_/datasets/coco_detection.py
Normal file
49
detection/configs/_base_/datasets/coco_detection.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# dataset settings
|
||||
dataset_type = 'CocoDataset'
|
||||
data_root = 'data/coco/'
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True),
|
||||
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
|
||||
]
|
||||
test_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(
|
||||
type='MultiScaleFlipAug',
|
||||
img_scale=(1333, 800),
|
||||
flip=False,
|
||||
transforms=[
|
||||
dict(type='Resize', keep_ratio=True),
|
||||
dict(type='RandomFlip'),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='ImageToTensor', keys=['img']),
|
||||
dict(type='Collect', keys=['img']),
|
||||
])
|
||||
]
|
||||
data = dict(
|
||||
samples_per_gpu=2,
|
||||
workers_per_gpu=2,
|
||||
train=dict(
|
||||
type=dataset_type,
|
||||
ann_file=data_root + 'annotations/instances_train2017.json',
|
||||
img_prefix=data_root + 'train2017/',
|
||||
pipeline=train_pipeline),
|
||||
val=dict(
|
||||
type=dataset_type,
|
||||
ann_file=data_root + 'annotations/instances_val2017.json',
|
||||
img_prefix=data_root + 'val2017/',
|
||||
pipeline=test_pipeline),
|
||||
test=dict(
|
||||
type=dataset_type,
|
||||
ann_file=data_root + 'annotations/instances_val2017.json',
|
||||
img_prefix=data_root + 'val2017/',
|
||||
pipeline=test_pipeline))
|
||||
evaluation = dict(interval=1, metric='bbox', classwise=True)
|
||||
49
detection/configs/_base_/datasets/coco_instance.py
Normal file
49
detection/configs/_base_/datasets/coco_instance.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# dataset settings
|
||||
dataset_type = 'CocoDataset'
|
||||
data_root = 'data/coco/'
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True, with_mask=True),
|
||||
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
||||
]
|
||||
test_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(
|
||||
type='MultiScaleFlipAug',
|
||||
img_scale=(1333, 800),
|
||||
flip=False,
|
||||
transforms=[
|
||||
dict(type='Resize', keep_ratio=True),
|
||||
dict(type='RandomFlip'),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='ImageToTensor', keys=['img']),
|
||||
dict(type='Collect', keys=['img']),
|
||||
])
|
||||
]
|
||||
data = dict(
|
||||
samples_per_gpu=2,
|
||||
workers_per_gpu=2,
|
||||
train=dict(
|
||||
type=dataset_type,
|
||||
ann_file=data_root + 'annotations/instances_train2017.json',
|
||||
img_prefix=data_root + 'train2017/',
|
||||
pipeline=train_pipeline),
|
||||
val=dict(
|
||||
type=dataset_type,
|
||||
ann_file=data_root + 'annotations/instances_val2017.json',
|
||||
img_prefix=data_root + 'val2017/',
|
||||
pipeline=test_pipeline),
|
||||
test=dict(
|
||||
type=dataset_type,
|
||||
ann_file=data_root + 'annotations/instances_val2017.json',
|
||||
img_prefix=data_root + 'val2017/',
|
||||
pipeline=test_pipeline))
|
||||
evaluation = dict(metric=['bbox', 'segm'], classwise=True)
|
||||
54
detection/configs/_base_/datasets/crowd_human.py
Normal file
54
detection/configs/_base_/datasets/crowd_human.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# dataset settings
|
||||
dataset_type = 'CrowdHumanDataset'
|
||||
data_root = 'data/CrowdHuman/'
|
||||
classes = ('person',)
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True),
|
||||
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
|
||||
]
|
||||
test_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(
|
||||
type='MultiScaleFlipAug',
|
||||
img_scale=(1333, 800),
|
||||
flip=False,
|
||||
transforms=[
|
||||
dict(type='Resize', keep_ratio=True),
|
||||
dict(type='RandomFlip'),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='ImageToTensor', keys=['img']),
|
||||
dict(type='Collect', keys=['img']),
|
||||
])
|
||||
]
|
||||
data = dict(
|
||||
samples_per_gpu=2,
|
||||
workers_per_gpu=2,
|
||||
train=dict(
|
||||
type=dataset_type,
|
||||
classes=classes,
|
||||
filter_empty_gt=True,
|
||||
ann_file=data_root + 'annotations/annotation_train.json',
|
||||
img_prefix=data_root + 'Images',
|
||||
pipeline=train_pipeline),
|
||||
val=dict(
|
||||
type=dataset_type,
|
||||
classes=classes,
|
||||
ann_file=data_root + 'annotations/annotation_val.json',
|
||||
img_prefix=data_root + 'Images',
|
||||
pipeline=test_pipeline),
|
||||
test=dict(
|
||||
type=dataset_type,
|
||||
classes=classes,
|
||||
ann_file=data_root + 'annotations/annotation_val.json',
|
||||
img_prefix=data_root + 'Images',
|
||||
pipeline=test_pipeline))
|
||||
evaluation = dict(interval=100, metric='bbox')
|
||||
16
detection/configs/_base_/default_runtime.py
Normal file
16
detection/configs/_base_/default_runtime.py
Normal file
@@ -0,0 +1,16 @@
|
||||
checkpoint_config = dict(interval=1)
|
||||
# yapf:disable
|
||||
log_config = dict(
|
||||
interval=50,
|
||||
hooks=[
|
||||
dict(type='TextLoggerHook'),
|
||||
# dict(type='TensorboardLoggerHook')
|
||||
])
|
||||
# yapf:enable
|
||||
custom_hooks = [dict(type='NumClassCheckHook')]
|
||||
|
||||
dist_params = dict(backend='nccl')
|
||||
log_level = 'INFO'
|
||||
load_from = None
|
||||
resume_from = None
|
||||
workflow = [('train', 1)]
|
||||
@@ -0,0 +1,208 @@
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
# All rights reserved.
|
||||
|
||||
# This source code is licensed under the license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
|
||||
# model settings
|
||||
model = dict(
|
||||
type='CascadeRCNN',
|
||||
pretrained=None,
|
||||
backbone=dict(
|
||||
type='ConvNeXt_speed',
|
||||
in_chans=3,
|
||||
depths=[3, 3, 9, 3],
|
||||
dims=[96, 192, 384, 768],
|
||||
drop_path_rate=0.2,
|
||||
layer_scale_init_value=1e-6,
|
||||
out_indices=[0, 1, 2, 3],
|
||||
),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[128, 256, 512, 1024],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=256,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[8],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[4, 8, 16, 32, 64]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='CascadeRoIHead',
|
||||
num_stages=3,
|
||||
stage_loss_weights=[1, 0.5, 0.25],
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
bbox_head=[
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0,
|
||||
loss_weight=1.0)),
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.05, 0.05, 0.1, 0.1]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0,
|
||||
loss_weight=1.0)),
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.033, 0.033, 0.067, 0.067]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))
|
||||
],
|
||||
mask_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
mask_head=dict(
|
||||
type='FCNMaskHead',
|
||||
num_convs=4,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
num_classes=80,
|
||||
loss_mask=dict(
|
||||
type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg = dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_across_levels=False,
|
||||
nms_pre=2000,
|
||||
nms_post=2000,
|
||||
max_per_img=2000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=[
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.6,
|
||||
neg_iou_thr=0.6,
|
||||
min_pos_iou=0.6,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.7,
|
||||
min_pos_iou=0.7,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False)
|
||||
]),
|
||||
test_cfg = dict(
|
||||
rpn=dict(
|
||||
nms_across_levels=False,
|
||||
nms_pre=1000,
|
||||
nms_post=1000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100,
|
||||
mask_thr_binary=0.5)))
|
||||
196
detection/configs/_base_/models/cascade_mask_rcnn_r50_fpn.py
Normal file
196
detection/configs/_base_/models/cascade_mask_rcnn_r50_fpn.py
Normal file
@@ -0,0 +1,196 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='CascadeRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=True,
|
||||
style='pytorch',
|
||||
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[256, 512, 1024, 2048],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=256,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[8],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[4, 8, 16, 32, 64]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='CascadeRoIHead',
|
||||
num_stages=3,
|
||||
stage_loss_weights=[1, 0.5, 0.25],
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
bbox_head=[
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0,
|
||||
loss_weight=1.0)),
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.05, 0.05, 0.1, 0.1]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0,
|
||||
loss_weight=1.0)),
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.033, 0.033, 0.067, 0.067]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))
|
||||
],
|
||||
mask_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
mask_head=dict(
|
||||
type='FCNMaskHead',
|
||||
num_convs=4,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
num_classes=80,
|
||||
loss_mask=dict(
|
||||
type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=2000,
|
||||
max_per_img=2000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=[
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.6,
|
||||
neg_iou_thr=0.6,
|
||||
min_pos_iou=0.6,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.7,
|
||||
min_pos_iou=0.7,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False)
|
||||
]),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=1000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100,
|
||||
mask_thr_binary=0.5)))
|
||||
@@ -0,0 +1,183 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='CascadeRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=True,
|
||||
style='pytorch',
|
||||
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[256, 512, 1024, 2048],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=256,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[8],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[4, 8, 16, 32, 64]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='CascadeRoIHead',
|
||||
num_stages=3,
|
||||
stage_loss_weights=[1, 0.5, 0.25],
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
bbox_head=[
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0,
|
||||
loss_weight=1.0)),
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.05, 0.05, 0.1, 0.1]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0,
|
||||
loss_weight=1.0)),
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.033, 0.033, 0.067, 0.067]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))
|
||||
],),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=2000,
|
||||
max_per_img=2000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=[
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.6,
|
||||
neg_iou_thr=0.6,
|
||||
min_pos_iou=0.6,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.7,
|
||||
min_pos_iou=0.7,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False)
|
||||
]),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=1000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100,
|
||||
mask_thr_binary=0.5)))
|
||||
179
detection/configs/_base_/models/cascade_rcnn_r50_fpn.py
Normal file
179
detection/configs/_base_/models/cascade_rcnn_r50_fpn.py
Normal file
@@ -0,0 +1,179 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='CascadeRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=True,
|
||||
style='pytorch',
|
||||
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[256, 512, 1024, 2048],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=256,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[8],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[4, 8, 16, 32, 64]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='CascadeRoIHead',
|
||||
num_stages=3,
|
||||
stage_loss_weights=[1, 0.5, 0.25],
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
bbox_head=[
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0,
|
||||
loss_weight=1.0)),
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.05, 0.05, 0.1, 0.1]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0,
|
||||
loss_weight=1.0)),
|
||||
dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.033, 0.033, 0.067, 0.067]),
|
||||
reg_class_agnostic=True,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))
|
||||
]),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=2000,
|
||||
max_per_img=2000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=[
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.6,
|
||||
neg_iou_thr=0.6,
|
||||
min_pos_iou=0.6,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.7,
|
||||
min_pos_iou=0.7,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
pos_weight=-1,
|
||||
debug=False)
|
||||
]),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=1000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100)))
|
||||
62
detection/configs/_base_/models/fast_rcnn_r50_fpn.py
Normal file
62
detection/configs/_base_/models/fast_rcnn_r50_fpn.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='FastRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=True,
|
||||
style='pytorch',
|
||||
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[256, 512, 1024, 2048],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
roi_head=dict(
|
||||
type='StandardRoIHead',
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
bbox_head=dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rcnn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100)))
|
||||
114
detection/configs/_base_/models/faster_rcnn_r50_caffe_c4.py
Normal file
114
detection/configs/_base_/models/faster_rcnn_r50_caffe_c4.py
Normal file
@@ -0,0 +1,114 @@
|
||||
# model settings
|
||||
norm_cfg = dict(type='BN', requires_grad=False)
|
||||
model = dict(
|
||||
type='FasterRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=3,
|
||||
strides=(1, 2, 2),
|
||||
dilations=(1, 1, 1),
|
||||
out_indices=(2, ),
|
||||
frozen_stages=1,
|
||||
norm_cfg=norm_cfg,
|
||||
norm_eval=True,
|
||||
style='caffe',
|
||||
init_cfg=dict(
|
||||
type='Pretrained',
|
||||
checkpoint='open-mmlab://detectron2/resnet50_caffe')),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=1024,
|
||||
feat_channels=1024,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[2, 4, 8, 16, 32],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[16]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='StandardRoIHead',
|
||||
shared_head=dict(
|
||||
type='ResLayer',
|
||||
depth=50,
|
||||
stage=3,
|
||||
stride=2,
|
||||
dilation=1,
|
||||
style='caffe',
|
||||
norm_cfg=norm_cfg,
|
||||
norm_eval=True),
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),
|
||||
out_channels=1024,
|
||||
featmap_strides=[16]),
|
||||
bbox_head=dict(
|
||||
type='BBoxHead',
|
||||
with_avg_pool=True,
|
||||
roi_feat_size=7,
|
||||
in_channels=2048,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=12000,
|
||||
max_per_img=2000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=6000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100)))
|
||||
105
detection/configs/_base_/models/faster_rcnn_r50_caffe_dc5.py
Normal file
105
detection/configs/_base_/models/faster_rcnn_r50_caffe_dc5.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# model settings
|
||||
norm_cfg = dict(type='BN', requires_grad=False)
|
||||
model = dict(
|
||||
type='FasterRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
strides=(1, 2, 2, 1),
|
||||
dilations=(1, 1, 1, 2),
|
||||
out_indices=(3, ),
|
||||
frozen_stages=1,
|
||||
norm_cfg=norm_cfg,
|
||||
norm_eval=True,
|
||||
style='caffe',
|
||||
init_cfg=dict(
|
||||
type='Pretrained',
|
||||
checkpoint='open-mmlab://detectron2/resnet50_caffe')),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=2048,
|
||||
feat_channels=2048,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[2, 4, 8, 16, 32],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[16]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='StandardRoIHead',
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=2048,
|
||||
featmap_strides=[16]),
|
||||
bbox_head=dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=2048,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=12000,
|
||||
max_per_img=2000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
nms_pre=6000,
|
||||
max_per_img=1000,
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100)))
|
||||
108
detection/configs/_base_/models/faster_rcnn_r50_fpn.py
Normal file
108
detection/configs/_base_/models/faster_rcnn_r50_fpn.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='FasterRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=True,
|
||||
style='pytorch',
|
||||
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[256, 512, 1024, 2048],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=256,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[8],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[4, 8, 16, 32, 64]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='StandardRoIHead',
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
bbox_head=dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=-1,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=2000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=1000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100)
|
||||
# soft-nms is also supported for rcnn testing
|
||||
# e.g., nms=dict(type='soft_nms', iou_threshold=0.5, min_score=0.05)
|
||||
))
|
||||
128
detection/configs/_base_/models/mask_rcnn_convnext_fpn.py
Normal file
128
detection/configs/_base_/models/mask_rcnn_convnext_fpn.py
Normal file
@@ -0,0 +1,128 @@
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
# All rights reserved.
|
||||
|
||||
# This source code is licensed under the license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
|
||||
# model settings
|
||||
model = dict(
|
||||
type='MaskRCNN',
|
||||
pretrained=None,
|
||||
backbone=dict(
|
||||
type='ConvNeXt',
|
||||
in_chans=3,
|
||||
depths=[3, 3, 9, 3],
|
||||
dims=[96, 192, 384, 768],
|
||||
drop_path_rate=0.2,
|
||||
layer_scale_init_value=1e-6,
|
||||
out_indices=[0, 1, 2, 3],
|
||||
),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[128, 256, 512, 1024],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=256,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[8],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[4, 8, 16, 32, 64]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='StandardRoIHead',
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
bbox_head=dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
mask_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
mask_head=dict(
|
||||
type='FCNMaskHead',
|
||||
num_convs=4,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
num_classes=80,
|
||||
loss_mask=dict(
|
||||
type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=-1,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=2000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=1000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100,
|
||||
mask_thr_binary=0.5)))
|
||||
125
detection/configs/_base_/models/mask_rcnn_r50_caffe_c4.py
Normal file
125
detection/configs/_base_/models/mask_rcnn_r50_caffe_c4.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# model settings
|
||||
norm_cfg = dict(type='BN', requires_grad=False)
|
||||
model = dict(
|
||||
type='MaskRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=3,
|
||||
strides=(1, 2, 2),
|
||||
dilations=(1, 1, 1),
|
||||
out_indices=(2, ),
|
||||
frozen_stages=1,
|
||||
norm_cfg=norm_cfg,
|
||||
norm_eval=True,
|
||||
style='caffe',
|
||||
init_cfg=dict(
|
||||
type='Pretrained',
|
||||
checkpoint='open-mmlab://detectron2/resnet50_caffe')),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=1024,
|
||||
feat_channels=1024,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[2, 4, 8, 16, 32],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[16]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='StandardRoIHead',
|
||||
shared_head=dict(
|
||||
type='ResLayer',
|
||||
depth=50,
|
||||
stage=3,
|
||||
stride=2,
|
||||
dilation=1,
|
||||
style='caffe',
|
||||
norm_cfg=norm_cfg,
|
||||
norm_eval=True),
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),
|
||||
out_channels=1024,
|
||||
featmap_strides=[16]),
|
||||
bbox_head=dict(
|
||||
type='BBoxHead',
|
||||
with_avg_pool=True,
|
||||
roi_feat_size=7,
|
||||
in_channels=2048,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
mask_roi_extractor=None,
|
||||
mask_head=dict(
|
||||
type='FCNMaskHead',
|
||||
num_convs=0,
|
||||
in_channels=2048,
|
||||
conv_out_channels=256,
|
||||
num_classes=80,
|
||||
loss_mask=dict(
|
||||
type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=12000,
|
||||
max_per_img=2000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=False,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=14,
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=6000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
max_per_img=1000,
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100,
|
||||
mask_thr_binary=0.5)))
|
||||
120
detection/configs/_base_/models/mask_rcnn_r50_fpn.py
Normal file
120
detection/configs/_base_/models/mask_rcnn_r50_fpn.py
Normal file
@@ -0,0 +1,120 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='MaskRCNN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=True,
|
||||
style='pytorch',
|
||||
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[256, 512, 1024, 2048],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=256,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[8],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[4, 8, 16, 32, 64]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
roi_head=dict(
|
||||
type='StandardRoIHead',
|
||||
bbox_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
bbox_head=dict(
|
||||
type='Shared2FCBBoxHead',
|
||||
in_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
mask_roi_extractor=dict(
|
||||
type='SingleRoIExtractor',
|
||||
roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),
|
||||
out_channels=256,
|
||||
featmap_strides=[4, 8, 16, 32]),
|
||||
mask_head=dict(
|
||||
type='FCNMaskHead',
|
||||
num_convs=4,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
num_classes=80,
|
||||
loss_mask=dict(
|
||||
type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=-1,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
rpn_proposal=dict(
|
||||
nms_pre=2000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.5,
|
||||
match_low_quality=True,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=512,
|
||||
pos_fraction=0.25,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=True),
|
||||
mask_size=28,
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=1000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0),
|
||||
rcnn=dict(
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100,
|
||||
mask_thr_binary=0.5)))
|
||||
60
detection/configs/_base_/models/retinanet_r50_fpn.py
Normal file
60
detection/configs/_base_/models/retinanet_r50_fpn.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='RetinaNet',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=True,
|
||||
style='pytorch',
|
||||
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[256, 512, 1024, 2048],
|
||||
out_channels=256,
|
||||
start_level=1,
|
||||
add_extra_convs='on_input',
|
||||
num_outs=5),
|
||||
bbox_head=dict(
|
||||
type='RetinaHead',
|
||||
num_classes=80,
|
||||
in_channels=256,
|
||||
stacked_convs=4,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
octave_base_scale=4,
|
||||
scales_per_octave=3,
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[8, 16, 32, 64, 128]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='FocalLoss',
|
||||
use_sigmoid=True,
|
||||
gamma=2.0,
|
||||
alpha=0.25,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.4,
|
||||
min_pos_iou=0,
|
||||
ignore_iof_thr=-1),
|
||||
allowed_border=-1,
|
||||
pos_weight=-1,
|
||||
debug=False),
|
||||
test_cfg=dict(
|
||||
nms_pre=1000,
|
||||
min_bbox_size=0,
|
||||
score_thr=0.05,
|
||||
nms=dict(type='nms', iou_threshold=0.5),
|
||||
max_per_img=100))
|
||||
58
detection/configs/_base_/models/rpn_r50_caffe_c4.py
Normal file
58
detection/configs/_base_/models/rpn_r50_caffe_c4.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='RPN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=3,
|
||||
strides=(1, 2, 2),
|
||||
dilations=(1, 1, 1),
|
||||
out_indices=(2, ),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=False),
|
||||
norm_eval=True,
|
||||
style='caffe',
|
||||
init_cfg=dict(
|
||||
type='Pretrained',
|
||||
checkpoint='open-mmlab://detectron2/resnet50_caffe')),
|
||||
neck=None,
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=1024,
|
||||
feat_channels=1024,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[2, 4, 8, 16, 32],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[16]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=12000,
|
||||
max_per_img=2000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0)))
|
||||
58
detection/configs/_base_/models/rpn_r50_fpn.py
Normal file
58
detection/configs/_base_/models/rpn_r50_fpn.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# model settings
|
||||
model = dict(
|
||||
type='RPN',
|
||||
backbone=dict(
|
||||
type='ResNet',
|
||||
depth=50,
|
||||
num_stages=4,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
frozen_stages=1,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=True,
|
||||
style='pytorch',
|
||||
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
|
||||
neck=dict(
|
||||
type='FPN',
|
||||
in_channels=[256, 512, 1024, 2048],
|
||||
out_channels=256,
|
||||
num_outs=5),
|
||||
rpn_head=dict(
|
||||
type='RPNHead',
|
||||
in_channels=256,
|
||||
feat_channels=256,
|
||||
anchor_generator=dict(
|
||||
type='AnchorGenerator',
|
||||
scales=[8],
|
||||
ratios=[0.5, 1.0, 2.0],
|
||||
strides=[4, 8, 16, 32, 64]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
rpn=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.7,
|
||||
neg_iou_thr=0.3,
|
||||
min_pos_iou=0.3,
|
||||
ignore_iof_thr=-1),
|
||||
sampler=dict(
|
||||
type='RandomSampler',
|
||||
num=256,
|
||||
pos_fraction=0.5,
|
||||
neg_pos_ub=-1,
|
||||
add_gt_as_proposals=False),
|
||||
allowed_border=0,
|
||||
pos_weight=-1,
|
||||
debug=False)),
|
||||
test_cfg=dict(
|
||||
rpn=dict(
|
||||
nms_pre=2000,
|
||||
max_per_img=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.7),
|
||||
min_bbox_size=0)))
|
||||
56
detection/configs/_base_/models/ssd300.py
Normal file
56
detection/configs/_base_/models/ssd300.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# model settings
|
||||
input_size = 300
|
||||
model = dict(
|
||||
type='SingleStageDetector',
|
||||
backbone=dict(
|
||||
type='SSDVGG',
|
||||
depth=16,
|
||||
with_last_pool=False,
|
||||
ceil_mode=True,
|
||||
out_indices=(3, 4),
|
||||
out_feature_indices=(22, 34),
|
||||
init_cfg=dict(
|
||||
type='Pretrained', checkpoint='open-mmlab://vgg16_caffe')),
|
||||
neck=dict(
|
||||
type='SSDNeck',
|
||||
in_channels=(512, 1024),
|
||||
out_channels=(512, 1024, 512, 256, 256, 256),
|
||||
level_strides=(2, 2, 1, 1),
|
||||
level_paddings=(1, 1, 0, 0),
|
||||
l2_norm_scale=20),
|
||||
bbox_head=dict(
|
||||
type='SSDHead',
|
||||
in_channels=(512, 1024, 512, 256, 256, 256),
|
||||
num_classes=80,
|
||||
anchor_generator=dict(
|
||||
type='SSDAnchorGenerator',
|
||||
scale_major=False,
|
||||
input_size=input_size,
|
||||
basesize_ratio_range=(0.15, 0.9),
|
||||
strides=[8, 16, 32, 64, 100, 300],
|
||||
ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]]),
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[.0, .0, .0, .0],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2])),
|
||||
# model training and testing settings
|
||||
train_cfg=dict(
|
||||
assigner=dict(
|
||||
type='MaxIoUAssigner',
|
||||
pos_iou_thr=0.5,
|
||||
neg_iou_thr=0.5,
|
||||
min_pos_iou=0.,
|
||||
ignore_iof_thr=-1,
|
||||
gt_max_assign_all=False),
|
||||
smoothl1_beta=1.,
|
||||
allowed_border=-1,
|
||||
pos_weight=-1,
|
||||
neg_pos_ratio=3,
|
||||
debug=False),
|
||||
test_cfg=dict(
|
||||
nms_pre=1000,
|
||||
nms=dict(type='nms', iou_threshold=0.45),
|
||||
min_bbox_size=0,
|
||||
score_thr=0.02,
|
||||
max_per_img=200))
|
||||
cudnn_benchmark = True
|
||||
11
detection/configs/_base_/schedules/schedule_1x.py
Normal file
11
detection/configs/_base_/schedules/schedule_1x.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# optimizer
|
||||
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# learning policy
|
||||
lr_config = dict(
|
||||
policy='step',
|
||||
warmup='linear',
|
||||
warmup_iters=500,
|
||||
warmup_ratio=0.001,
|
||||
step=[8, 11])
|
||||
runner = dict(type='EpochBasedRunner', max_epochs=12)
|
||||
11
detection/configs/_base_/schedules/schedule_3x.py
Normal file
11
detection/configs/_base_/schedules/schedule_3x.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# optimizer
|
||||
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# learning policy
|
||||
lr_config = dict(
|
||||
policy='step',
|
||||
warmup='linear',
|
||||
warmup_iters=500,
|
||||
warmup_ratio=0.001,
|
||||
step=[27, 33])
|
||||
runner = dict(type='EpochBasedRunner', max_epochs=36)
|
||||
51
detection/configs/coco/README.md
Normal file
51
detection/configs/coco/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# COCO
|
||||
|
||||
## Introduction
|
||||
|
||||
Introduced by Lin et al. in [Microsoft COCO: Common Objects in Context](https://arxiv.org/pdf/1405.0312v3.pdf)
|
||||
|
||||
The MS COCO (Microsoft Common Objects in Context) dataset is a large-scale object detection, segmentation, key-point detection, and captioning dataset. The dataset consists of 328K images.
|
||||
|
||||
Splits: The first version of MS COCO dataset was released in 2014. It contains 164K images split into training (83K), validation (41K) and test (41K) sets. In 2015 additional test set of 81K images was released, including all the previous test images and 40K new images.
|
||||
|
||||
Based on community feedback, in 2017 the training/validation split was changed from 83K/41K to 118K/5K. The new split uses the same images and annotations. The 2017 test set is a subset of 41K images of the 2015 test set. Additionally, the 2017 release contains a new unannotated dataset of 123K images.
|
||||
|
||||
|
||||
## Model Zoo
|
||||
|
||||
### Mask R-CNN + FlashInternImage
|
||||
|
||||
|
||||
| backbone | schd | box mAP | mask mAP |Config | Download |
|
||||
| :-----------------: | :---------: | :-----: |:------: | :-----: | :---: |
|
||||
| FlashInternImage-T | 1x | 48.0 | 43.1 | [config](./mask_rcnn_flash_intern_image_t_fpn_1x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_t_fpn_1x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_t_fpn_1x_coco.log) |
|
||||
| FlashInternImage-T | 3x | 49.5 | 44.0 | [config](././mask_rcnn_flash_intern_image_t_fpn_3x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_t_fpn_3x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_t_fpn_3x_coco.log) |
|
||||
| FlashInternImage-S | 1x | 49.2 | 44.0 | [config](./mask_rcnn_flash_intern_image_s_fpn_1x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_s_fpn_1x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_s_fpn_1x_coco.log) |
|
||||
| FlashInternImage-S | 3x | 50.5 | 44.9 | [config](./mask_rcnn_flash_intern_image_s_fpn_3x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_s_fpn_3x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_s_fpn_3x_coco.log) |
|
||||
| FlashInternImage-B | 1x | 50.1 | 44.5 | [config](./mask_rcnn_flash_intern_image_b_fpn_1x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_b_fpn_1x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_b_fpn_1x_coco.log) |
|
||||
| FlashInternImage-B | 3x | 50.6 | 45.4 | [config](./mask_rcnn_flash_intern_image_b_fpn_3x_coco.py)| [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_b_fpn_3x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/mask_rcnn_flash_internimage_b_fpn_3x_coco.log) |
|
||||
|
||||
- Training speed is measured with A100 GPUs using current code and may be faster than the speed in logs.
|
||||
- Some logs are our recent newly trained ones. There might be slight differences between the results in logs and our paper.
|
||||
- Please set `with_cp=True` to save memory if you meet `out-of-memory` issues.
|
||||
|
||||
### Cascade Mask R-CNN + FlashInternImage
|
||||
|
||||
| backbone | schd | box mAP | mask mAP | Config | Download |
|
||||
| :------------: | :---------: | :-----: | :------: | :---: | :---: |
|
||||
| FlashInternImage-L | 1x | 55.6 | 48.2 | [config](./cascade_flash_intern_image_l_fpn_1x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/cascade_flash_internimage_l_fpn_1x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/cascade_flash_internimage_l_fpn_1x_coco.log)
|
||||
| FlashInternImage-L | 3x | 56.7 | 48.9 | [config](./cascade_flash_intern_image_l_fpn_3x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/cascade_flash_internimage_l_fpn_3x_coco.pth) |
|
||||
|
||||
- Training speed is measured with A100 GPUs using current code and may be faster than the speed in logs.
|
||||
- Some logs are our recent newly trained ones. There might be slight differences between the results in logs and our paper.
|
||||
- Please set `with_cp=True` to save memory if you meet `out-of-memory` issues.
|
||||
|
||||
|
||||
### DINO + FlashInternImage
|
||||
| backbone | lr type | pretrain | schd | box mAP | Config | Download |
|
||||
| :------------: | :---------: |:---------: | :---------: | :-----: | :---: | :-----:
|
||||
| FlashInternImage-T | layer-wise lr | ImageNet-1K | 1x | 54.7 | [config](./dino_4scale_flash_internimage_t_1x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/dino_4scale_flash_internimage_t_1x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/dino_4scale_flash_internimage_t_1x_coco.json) |
|
||||
| FlashInternImage-S | layer-wise lr | ImageNet-1K | 1x | 55.3 | [config](./dino_4scale_flash_internimage_s_1x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/dino_4scale_flash_internimage_s_1x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/dino_4scale_flash_internimage_s_1x_coco.log) |
|
||||
| FlashInternImage-B | layer-wise lr | ImageNet-1K | 1x | 56.0 | [config](./dino_4scale_flash_internimage_b_1x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/dino_4scale_flash_internimage_b_1x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/dino_4scale_flash_internimage_b_1x_coco.log) |
|
||||
| FlashInternImage-L | 0.1x backbone lr | ImageNet-22K | 1x | 58.8 | [config](./dino_4scale_flash_internimage_l_1x_coco.py) | [ckpt](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/dino_4scale_flash_internimage_l_1x_coco.pth) \| [log](https://huggingface.co/OpenGVLab/DCNv4/resolve/main/dino_4scale_flash_internimage_l_1x_coco.log) |
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/models/cascade_mask_rcnn_r50_fpn.py',
|
||||
'../_base_/datasets/coco_instance.py',
|
||||
'../_base_/schedules/schedule_1x.py',
|
||||
'../_base_/default_runtime.py'
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_l_22k_384.pth'
|
||||
model = dict(
|
||||
backbone=dict(
|
||||
_delete_=True,
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=160,
|
||||
depths=[5, 5, 22, 5],
|
||||
groups=[10, 20, 40, 80],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.4,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=2.0,
|
||||
post_norm=True,
|
||||
with_cp=False,
|
||||
dcn_output_bias=True,
|
||||
mlp_fc2_bias=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
# We leverage the FPN implemented in ViTDet for stable training,
|
||||
# and we don't benefit from this FPN in terms of performance.
|
||||
neck=dict(
|
||||
type='FPN_vitdet',
|
||||
in_channels=[160, 320, 640, 1280],
|
||||
out_channels=256,
|
||||
norm_cfg=dict(type='LN', requires_grad=True),
|
||||
use_residual=True,
|
||||
num_outs=5),
|
||||
roi_head=dict(
|
||||
bbox_head=[
|
||||
dict(
|
||||
type='DCNv4FCBBoxHead',
|
||||
with_dcn=False,
|
||||
num_shared_convs=4,
|
||||
num_shared_fcs=1,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
reg_decoded_bbox=True,
|
||||
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),
|
||||
dict(
|
||||
type='DCNv4FCBBoxHead',
|
||||
with_dcn=False,
|
||||
num_shared_convs=4,
|
||||
num_shared_fcs=1,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.05, 0.05, 0.1, 0.1]),
|
||||
reg_class_agnostic=False,
|
||||
reg_decoded_bbox=True,
|
||||
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),
|
||||
dict(
|
||||
type='DCNv4FCBBoxHead',
|
||||
with_dcn=False,
|
||||
num_shared_convs=4,
|
||||
num_shared_fcs=1,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.033, 0.033, 0.067, 0.067]),
|
||||
reg_class_agnostic=False,
|
||||
reg_decoded_bbox=True,
|
||||
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='GIoULoss', loss_weight=10.0))
|
||||
]))
|
||||
# By default, models are trained on 8 GPUs with 2 images per GPU
|
||||
data = dict(samples_per_gpu=2)
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=37, layer_decay_rate=0.94,
|
||||
depths=[5, 5, 22, 5]))
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# fp16 = dict(loss_scale=dict(init_scale=512))
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
|
||||
# Bbox
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.556
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.744
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.604
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.388
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.598
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.720
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.670
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.670
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.670
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.505
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.714
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.823
|
||||
|
||||
# Segm
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.482
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.720
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.526
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.289
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.514
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.676
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.588
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.588
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.588
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.424
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.629
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.749
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/models/cascade_mask_rcnn_r50_fpn.py',
|
||||
'../_base_/datasets/coco_instance.py',
|
||||
'../_base_/schedules/schedule_3x.py',
|
||||
'../_base_/default_runtime.py'
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_l_22k_384.pth'
|
||||
model = dict(
|
||||
backbone=dict(
|
||||
_delete_=True,
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=160,
|
||||
depths=[5, 5, 22, 5],
|
||||
groups=[10, 20, 40, 80],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.4,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=2.0,
|
||||
post_norm=True,
|
||||
with_cp=True,
|
||||
dcn_output_bias=True,
|
||||
mlp_fc2_bias=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
# We leverage the FPN implemented in ViTDet for stable training,
|
||||
# and we don't benefit from this FPN in terms of performance.
|
||||
neck=dict(
|
||||
type='FPN_vitdet',
|
||||
in_channels=[160, 320, 640, 1280],
|
||||
out_channels=256,
|
||||
norm_cfg=dict(type='LN', requires_grad=True),
|
||||
use_residual=True,
|
||||
num_outs=5),
|
||||
roi_head=dict(
|
||||
bbox_head=[
|
||||
dict(
|
||||
type='ConvFCBBoxHead',
|
||||
num_shared_convs=4,
|
||||
num_shared_fcs=1,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
||||
reg_class_agnostic=False,
|
||||
reg_decoded_bbox=True,
|
||||
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),
|
||||
dict(
|
||||
type='ConvFCBBoxHead',
|
||||
num_shared_convs=4,
|
||||
num_shared_fcs=1,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.05, 0.05, 0.1, 0.1]),
|
||||
reg_class_agnostic=False,
|
||||
reg_decoded_bbox=True,
|
||||
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),
|
||||
dict(
|
||||
type='ConvFCBBoxHead',
|
||||
num_shared_convs=4,
|
||||
num_shared_fcs=1,
|
||||
in_channels=256,
|
||||
conv_out_channels=256,
|
||||
fc_out_channels=1024,
|
||||
roi_feat_size=7,
|
||||
num_classes=80,
|
||||
bbox_coder=dict(
|
||||
type='DeltaXYWHBBoxCoder',
|
||||
target_means=[0., 0., 0., 0.],
|
||||
target_stds=[0.033, 0.033, 0.067, 0.067]),
|
||||
reg_class_agnostic=False,
|
||||
reg_decoded_bbox=True,
|
||||
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
||||
loss_bbox=dict(type='GIoULoss', loss_weight=10.0))
|
||||
]))
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
# augmentation strategy originates from DETR / Sparse RCNN
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True, with_mask=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(type='AutoAugment',
|
||||
policies=[
|
||||
[
|
||||
dict(type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),
|
||||
(608, 1333), (640, 1333), (672, 1333), (704, 1333),
|
||||
(736, 1333), (768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True)
|
||||
],
|
||||
[
|
||||
dict(type='Resize',
|
||||
img_scale=[(400, 1333), (500, 1333), (600, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True),
|
||||
dict(type='RandomCrop',
|
||||
crop_type='absolute_range',
|
||||
crop_size=(384, 600),
|
||||
allow_negative_crop=True),
|
||||
dict(type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
override=True,
|
||||
keep_ratio=True)
|
||||
]
|
||||
]),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
||||
]
|
||||
# we use 4 nodes to train this model, with a total batch size of 64
|
||||
data = dict(
|
||||
samples_per_gpu=4,
|
||||
train=dict(pipeline=train_pipeline))
|
||||
# optimizer
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001 * 2, weight_decay=0.05,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=37, layer_decay_rate=0.90,
|
||||
depths=[5, 5, 22, 5], offset_lr_scale=0.01))
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# fp16 = dict(loss_scale=dict(init_scale=512))
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
resume_from = None
|
||||
custom_hooks = [
|
||||
dict(
|
||||
type='ExpMomentumEMAHook',
|
||||
resume_from=resume_from,
|
||||
momentum=0.0001,
|
||||
priority=49)
|
||||
]
|
||||
|
||||
# Bbox
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.567
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.754
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.615
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.410
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.612
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.729
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.685
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.685
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.685
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.532
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.733
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.825
|
||||
|
||||
# Segm
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.490
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.732
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.537
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.301
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.527
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.677
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.600
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.600
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.600
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.445
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.644
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.751
|
||||
@@ -0,0 +1,180 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/datasets/coco_detection.py',
|
||||
'../_base_/default_runtime.py',
|
||||
'../_base_/schedules/schedule_1x.py',
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_b_1k_224.pth'
|
||||
model = dict(
|
||||
type='DINO',
|
||||
backbone=dict(
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=112,
|
||||
depths=[4, 4, 21, 4],
|
||||
groups=[7, 14, 28, 56],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.3,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=0.5,
|
||||
post_norm=True,
|
||||
with_cp=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
neck=dict(
|
||||
type='ChannelMapper',
|
||||
in_channels=[224, 448, 896],
|
||||
kernel_size=1,
|
||||
out_channels=256,
|
||||
act_cfg=None,
|
||||
norm_cfg=dict(type='GN', num_groups=32),
|
||||
num_outs=4),
|
||||
bbox_head=dict(
|
||||
type='DINOHead',
|
||||
num_query=900,
|
||||
num_classes=80,
|
||||
in_channels=2048,
|
||||
sync_cls_avg_factor=True,
|
||||
as_two_stage=True,
|
||||
with_box_refine=True,
|
||||
dn_cfg=dict(
|
||||
type='CdnQueryGenerator',
|
||||
noise_scale=dict(label=0.5, box=1.0),
|
||||
group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),
|
||||
transformer=dict(
|
||||
type='DinoTransformer',
|
||||
two_stage_num_proposals=900,
|
||||
encoder=dict(
|
||||
type='DetrTransformerEncoder',
|
||||
num_layers=6,
|
||||
transformerlayers=dict(
|
||||
type='BaseTransformerLayer',
|
||||
attn_cfgs=dict(
|
||||
type='MultiScaleDeformableAttention',
|
||||
embed_dims=256,
|
||||
dropout=0.0),
|
||||
feedforward_channels=2048,
|
||||
ffn_dropout=0.0, # 0.1 for DeformDETR
|
||||
|
||||
operation_order=('self_attn', 'norm', 'ffn', 'norm'))),
|
||||
decoder=dict(
|
||||
type='DinoTransformerDecoder',
|
||||
num_layers=6,
|
||||
return_intermediate=True,
|
||||
transformerlayers=dict(
|
||||
type='DetrTransformerDecoderLayer',
|
||||
attn_cfgs=[
|
||||
dict(
|
||||
type='MultiheadAttention',
|
||||
embed_dims=256,
|
||||
num_heads=8,
|
||||
dropout=0.0),
|
||||
dict(
|
||||
type='MultiScaleDeformableAttention',
|
||||
embed_dims=256,
|
||||
dropout=0.0),
|
||||
],
|
||||
feedforward_channels=2048,
|
||||
ffn_dropout=0.0,
|
||||
operation_order=('self_attn', 'norm', 'cross_attn', 'norm',
|
||||
'ffn', 'norm')))),
|
||||
positional_encoding=dict(
|
||||
type='SinePositionalEncoding',
|
||||
num_feats=128,
|
||||
temperature=20,
|
||||
normalize=True),
|
||||
loss_cls=dict(
|
||||
type='FocalLoss',
|
||||
use_sigmoid=True,
|
||||
gamma=2.0,
|
||||
alpha=0.25,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=5.0),
|
||||
loss_iou=dict(type='GIoULoss', loss_weight=2.0)),
|
||||
# training and testing settings
|
||||
train_cfg=dict(
|
||||
assigner=dict(
|
||||
type='HungarianAssigner',
|
||||
cls_cost=dict(type='FocalLossCost', weight=2.0),
|
||||
reg_cost=dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),
|
||||
iou_cost=dict(type='IoUCost', iou_mode='giou', weight=2.0))),
|
||||
test_cfg=dict(max_per_img=300))
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different
|
||||
# from the default setting in mmdet.
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(
|
||||
type='AutoAugment',
|
||||
policies=[
|
||||
[
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True)
|
||||
],
|
||||
[
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(400, 4200), (500, 4200), (600, 4200)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True),
|
||||
dict(
|
||||
type='RandomCrop',
|
||||
crop_type='absolute_range',
|
||||
crop_size=(384, 600),
|
||||
allow_negative_crop=False),
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
override=True,
|
||||
keep_ratio=True)
|
||||
]
|
||||
]),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
|
||||
]
|
||||
# By default, models are trained on 8 GPUs with 4 images per GPU
|
||||
data = dict(
|
||||
samples_per_gpu=4,
|
||||
train=dict(pipeline=train_pipeline))
|
||||
# optimizer
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.0001,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=33, layer_decay_rate=0.9,
|
||||
depths=[4, 4, 21, 4]))
|
||||
optimizer_config = dict(_delete_=True, grad_clip=dict(max_norm=0.1, norm_type=2))
|
||||
# learning policy
|
||||
lr_config = dict(
|
||||
policy='step',
|
||||
warmup='linear',
|
||||
warmup_iters=500,
|
||||
warmup_ratio=0.001,
|
||||
step=[11])
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
# fp16 = dict(loss_scale=512.)
|
||||
@@ -0,0 +1,184 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/datasets/coco_detection.py',
|
||||
'../_base_/default_runtime.py',
|
||||
'../_base_/schedules/schedule_1x.py',
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_l_22k_384.pth'
|
||||
model = dict(
|
||||
type='DINO',
|
||||
backbone=dict(
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=160,
|
||||
depths=[5, 5, 22, 5],
|
||||
groups=[10, 20, 40, 80],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.4,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=2.0,
|
||||
post_norm=True,
|
||||
with_cp=True,
|
||||
dcn_output_bias=True,
|
||||
mlp_fc2_bias=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
neck=dict(
|
||||
type='ChannelMapper',
|
||||
in_channels=[320, 640, 1280],
|
||||
kernel_size=1,
|
||||
out_channels=256,
|
||||
act_cfg=None,
|
||||
# norm_cfg=norm_cfg,
|
||||
norm_cfg=dict(type='GN', num_groups=32),
|
||||
num_outs=4),
|
||||
bbox_head=dict(
|
||||
type='DINOHead',
|
||||
num_query=900,
|
||||
num_classes=80,
|
||||
in_channels=2048,
|
||||
sync_cls_avg_factor=True,
|
||||
as_two_stage=True,
|
||||
with_box_refine=True,
|
||||
dn_cfg=dict(
|
||||
type='CdnQueryGenerator',
|
||||
noise_scale=dict(label=0.5, box=1.0),
|
||||
group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),
|
||||
transformer=dict(
|
||||
type='DinoTransformer',
|
||||
two_stage_num_proposals=900,
|
||||
encoder=dict(
|
||||
type='DetrTransformerEncoder',
|
||||
num_layers=6,
|
||||
transformerlayers=dict(
|
||||
type='BaseTransformerLayer',
|
||||
attn_cfgs=dict(
|
||||
type='MultiScaleDeformableAttention',
|
||||
embed_dims=256,
|
||||
dropout=0.0),
|
||||
feedforward_channels=2048,
|
||||
ffn_dropout=0.0, # 0.1 for DeformDETR
|
||||
|
||||
operation_order=('self_attn', 'norm', 'ffn', 'norm'))),
|
||||
decoder=dict(
|
||||
type='DinoTransformerDecoder',
|
||||
num_layers=6,
|
||||
return_intermediate=True,
|
||||
transformerlayers=dict(
|
||||
type='DetrTransformerDecoderLayer',
|
||||
attn_cfgs=[
|
||||
dict(
|
||||
type='MultiheadAttention',
|
||||
embed_dims=256,
|
||||
num_heads=8,
|
||||
dropout=0.0),
|
||||
dict(
|
||||
type='MultiScaleDeformableAttention',
|
||||
embed_dims=256,
|
||||
dropout=0.0),
|
||||
],
|
||||
feedforward_channels=2048,
|
||||
ffn_dropout=0.0,
|
||||
operation_order=('self_attn', 'norm', 'cross_attn', 'norm',
|
||||
'ffn', 'norm')))),
|
||||
positional_encoding=dict(
|
||||
type='SinePositionalEncoding',
|
||||
num_feats=128,
|
||||
temperature=20,
|
||||
normalize=True),
|
||||
loss_cls=dict(
|
||||
type='FocalLoss',
|
||||
use_sigmoid=True,
|
||||
gamma=2.0,
|
||||
alpha=0.25,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=5.0),
|
||||
loss_iou=dict(type='GIoULoss', loss_weight=2.0)),
|
||||
# training and testing settings
|
||||
train_cfg=dict(
|
||||
assigner=dict(
|
||||
type='HungarianAssigner',
|
||||
cls_cost=dict(type='FocalLossCost', weight=2.0),
|
||||
reg_cost=dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),
|
||||
iou_cost=dict(type='IoUCost', iou_mode='giou', weight=2.0))),
|
||||
test_cfg=dict(max_per_img=300))
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different
|
||||
# from the default setting in mmdet.
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(
|
||||
type='AutoAugment',
|
||||
policies=[
|
||||
[
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True)
|
||||
],
|
||||
[
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(400, 4200), (500, 4200), (600, 4200)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True),
|
||||
dict(
|
||||
type='RandomCrop',
|
||||
crop_type='absolute_range',
|
||||
crop_size=(384, 600),
|
||||
allow_negative_crop=False),
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
override=True,
|
||||
keep_ratio=True)
|
||||
]
|
||||
]),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
|
||||
]
|
||||
# By default, models are trained on 8 GPUs with 2 images per GPU
|
||||
data = dict(
|
||||
samples_per_gpu=2,
|
||||
train=dict(pipeline=train_pipeline))
|
||||
# optimizer
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05,
|
||||
paramwise_cfg=dict(
|
||||
custom_keys={
|
||||
'backbone': dict(lr_mult=0.1),
|
||||
}))
|
||||
optimizer_config = dict(_delete_=True, grad_clip=dict(max_norm=0.1, norm_type=2))
|
||||
# learning policy
|
||||
lr_config = dict(
|
||||
policy='step',
|
||||
warmup='linear',
|
||||
warmup_iters=500,
|
||||
warmup_ratio=0.001,
|
||||
step=[11])
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
# fp16 = dict(loss_scale=512.)
|
||||
@@ -0,0 +1,180 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/datasets/coco_detection.py',
|
||||
'../_base_/default_runtime.py',
|
||||
'../_base_/schedules/schedule_1x.py',
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_s_1k_224.pth'
|
||||
model = dict(
|
||||
type='DINO',
|
||||
backbone=dict(
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=80,
|
||||
depths=[4, 4, 21, 4],
|
||||
groups=[5, 10, 20, 40],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.3,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=1.0,
|
||||
post_norm=True,
|
||||
with_cp=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
neck=dict(
|
||||
type='ChannelMapper',
|
||||
in_channels=[160, 320, 640],
|
||||
kernel_size=1,
|
||||
out_channels=256,
|
||||
act_cfg=None,
|
||||
norm_cfg=dict(type='GN', num_groups=32),
|
||||
num_outs=4),
|
||||
bbox_head=dict(
|
||||
type='DINOHead',
|
||||
num_query=900,
|
||||
num_classes=80,
|
||||
in_channels=2048,
|
||||
sync_cls_avg_factor=True,
|
||||
as_two_stage=True,
|
||||
with_box_refine=True,
|
||||
dn_cfg=dict(
|
||||
type='CdnQueryGenerator',
|
||||
noise_scale=dict(label=0.5, box=1.0),
|
||||
group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),
|
||||
transformer=dict(
|
||||
type='DinoTransformer',
|
||||
two_stage_num_proposals=900,
|
||||
encoder=dict(
|
||||
type='DetrTransformerEncoder',
|
||||
num_layers=6,
|
||||
transformerlayers=dict(
|
||||
type='BaseTransformerLayer',
|
||||
attn_cfgs=dict(
|
||||
type='MultiScaleDeformableAttention',
|
||||
embed_dims=256,
|
||||
dropout=0.0),
|
||||
feedforward_channels=2048,
|
||||
ffn_dropout=0.0, # 0.1 for DeformDETR
|
||||
|
||||
operation_order=('self_attn', 'norm', 'ffn', 'norm'))),
|
||||
decoder=dict(
|
||||
type='DinoTransformerDecoder',
|
||||
num_layers=6,
|
||||
return_intermediate=True,
|
||||
transformerlayers=dict(
|
||||
type='DetrTransformerDecoderLayer',
|
||||
attn_cfgs=[
|
||||
dict(
|
||||
type='MultiheadAttention',
|
||||
embed_dims=256,
|
||||
num_heads=8,
|
||||
dropout=0.0),
|
||||
dict(
|
||||
type='MultiScaleDeformableAttention',
|
||||
embed_dims=256,
|
||||
dropout=0.0),
|
||||
],
|
||||
feedforward_channels=2048,
|
||||
ffn_dropout=0.0,
|
||||
operation_order=('self_attn', 'norm', 'cross_attn', 'norm',
|
||||
'ffn', 'norm')))),
|
||||
positional_encoding=dict(
|
||||
type='SinePositionalEncoding',
|
||||
num_feats=128,
|
||||
temperature=20,
|
||||
normalize=True),
|
||||
loss_cls=dict(
|
||||
type='FocalLoss',
|
||||
use_sigmoid=True,
|
||||
gamma=2.0,
|
||||
alpha=0.25,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=5.0),
|
||||
loss_iou=dict(type='GIoULoss', loss_weight=2.0)),
|
||||
# training and testing settings
|
||||
train_cfg=dict(
|
||||
assigner=dict(
|
||||
type='HungarianAssigner',
|
||||
cls_cost=dict(type='FocalLossCost', weight=2.0),
|
||||
reg_cost=dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),
|
||||
iou_cost=dict(type='IoUCost', iou_mode='giou', weight=2.0))),
|
||||
test_cfg=dict(max_per_img=300))
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different
|
||||
# from the default setting in mmdet.
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(
|
||||
type='AutoAugment',
|
||||
policies=[
|
||||
[
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True)
|
||||
],
|
||||
[
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(400, 4200), (500, 4200), (600, 4200)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True),
|
||||
dict(
|
||||
type='RandomCrop',
|
||||
crop_type='absolute_range',
|
||||
crop_size=(384, 600),
|
||||
allow_negative_crop=False),
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
override=True,
|
||||
keep_ratio=True)
|
||||
]
|
||||
]),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
|
||||
]
|
||||
# By default, models are trained on 8 GPUs with 4 images per GPU
|
||||
data = dict(
|
||||
samples_per_gpu=4,
|
||||
train=dict(pipeline=train_pipeline))
|
||||
# optimizer
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.0001,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=33, layer_decay_rate=0.9,
|
||||
depths=[4, 4, 21, 4]))
|
||||
optimizer_config = dict(_delete_=True, grad_clip=dict(max_norm=0.1, norm_type=2))
|
||||
# learning policy
|
||||
lr_config = dict(
|
||||
policy='step',
|
||||
warmup='linear',
|
||||
warmup_iters=500,
|
||||
warmup_ratio=0.001,
|
||||
step=[11])
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
# fp16 = dict(loss_scale=512.)
|
||||
@@ -0,0 +1,179 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/datasets/coco_detection.py',
|
||||
'../_base_/default_runtime.py',
|
||||
'../_base_/schedules/schedule_1x.py',
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_t_1k_224.pth'
|
||||
model = dict(
|
||||
type='DINO',
|
||||
backbone=dict(
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=64,
|
||||
depths=[4, 4, 18, 4],
|
||||
groups=[4, 8, 16, 32],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.2,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=1.0,
|
||||
post_norm=False,
|
||||
with_cp=True,
|
||||
out_indices=(1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
neck=dict(
|
||||
type='ChannelMapper',
|
||||
in_channels=[128, 256, 512],
|
||||
kernel_size=1,
|
||||
out_channels=256,
|
||||
act_cfg=None,
|
||||
norm_cfg=dict(type='GN', num_groups=32),
|
||||
num_outs=4),
|
||||
bbox_head=dict(
|
||||
type='DINOHead',
|
||||
num_query=900,
|
||||
num_classes=80,
|
||||
in_channels=2048,
|
||||
sync_cls_avg_factor=True,
|
||||
as_two_stage=True,
|
||||
with_box_refine=True,
|
||||
dn_cfg=dict(
|
||||
type='CdnQueryGenerator',
|
||||
noise_scale=dict(label=0.5, box=1.0),
|
||||
group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),
|
||||
transformer=dict(
|
||||
type='DinoTransformer',
|
||||
two_stage_num_proposals=900,
|
||||
encoder=dict(
|
||||
type='DetrTransformerEncoder',
|
||||
num_layers=6,
|
||||
transformerlayers=dict(
|
||||
type='BaseTransformerLayer',
|
||||
attn_cfgs=dict(
|
||||
type='MultiScaleDeformableAttention',
|
||||
embed_dims=256,
|
||||
dropout=0.0),
|
||||
feedforward_channels=2048,
|
||||
ffn_dropout=0.0, # 0.1 for DeformDETR
|
||||
|
||||
operation_order=('self_attn', 'norm', 'ffn', 'norm'))),
|
||||
decoder=dict(
|
||||
type='DinoTransformerDecoder',
|
||||
num_layers=6,
|
||||
return_intermediate=True,
|
||||
transformerlayers=dict(
|
||||
type='DetrTransformerDecoderLayer',
|
||||
attn_cfgs=[
|
||||
dict(
|
||||
type='MultiheadAttention',
|
||||
embed_dims=256,
|
||||
num_heads=8,
|
||||
dropout=0.0),
|
||||
dict(
|
||||
type='MultiScaleDeformableAttention',
|
||||
embed_dims=256,
|
||||
dropout=0.0),
|
||||
],
|
||||
feedforward_channels=2048,
|
||||
ffn_dropout=0.0,
|
||||
operation_order=('self_attn', 'norm', 'cross_attn', 'norm',
|
||||
'ffn', 'norm')))),
|
||||
positional_encoding=dict(
|
||||
type='SinePositionalEncoding',
|
||||
num_feats=128,
|
||||
temperature=20,
|
||||
normalize=True),
|
||||
loss_cls=dict(
|
||||
type='FocalLoss',
|
||||
use_sigmoid=True,
|
||||
gamma=2.0,
|
||||
alpha=0.25,
|
||||
loss_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=5.0),
|
||||
loss_iou=dict(type='GIoULoss', loss_weight=2.0)),
|
||||
# training and testing settings
|
||||
train_cfg=dict(
|
||||
assigner=dict(
|
||||
type='HungarianAssigner',
|
||||
cls_cost=dict(type='FocalLossCost', weight=2.0),
|
||||
reg_cost=dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),
|
||||
iou_cost=dict(type='IoUCost', iou_mode='giou', weight=2.0))),
|
||||
test_cfg=dict(max_per_img=300))
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different
|
||||
# from the default setting in mmdet.
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(
|
||||
type='AutoAugment',
|
||||
policies=[
|
||||
[
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True)
|
||||
],
|
||||
[
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(400, 4200), (500, 4200), (600, 4200)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True),
|
||||
dict(
|
||||
type='RandomCrop',
|
||||
crop_type='absolute_range',
|
||||
crop_size=(384, 600),
|
||||
allow_negative_crop=False),
|
||||
dict(
|
||||
type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
override=True,
|
||||
keep_ratio=True)
|
||||
]
|
||||
]),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
|
||||
]
|
||||
# By default, models are trained on 8 GPUs with 4 images per GPU
|
||||
data = dict(
|
||||
samples_per_gpu=4,
|
||||
train=dict(pipeline=train_pipeline))
|
||||
# optimizer
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.0001,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=30, layer_decay_rate=0.9,
|
||||
depths=[4, 4, 18, 4]))
|
||||
optimizer_config = dict(_delete_=True, grad_clip=dict(max_norm=0.1, norm_type=2))
|
||||
# learning policy
|
||||
lr_config = dict(
|
||||
policy='step',
|
||||
warmup='linear',
|
||||
warmup_iters=500,
|
||||
warmup_ratio=0.001,
|
||||
step=[11])
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
# fp16 = dict(loss_scale=512.)
|
||||
@@ -0,0 +1,83 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/models/mask_rcnn_r50_fpn.py',
|
||||
'../_base_/datasets/coco_instance.py',
|
||||
'../_base_/schedules/schedule_1x.py',
|
||||
'../_base_/default_runtime.py'
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_b_1k_224.pth'
|
||||
model = dict(
|
||||
backbone=dict(
|
||||
_delete_=True,
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=112,
|
||||
depths=[4, 4, 21, 4],
|
||||
groups=[7, 14, 28, 56],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.3,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=0.5,
|
||||
post_norm=True,
|
||||
with_cp=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
# We leverage the FPN implemented in ViTDet for stable training,
|
||||
# and we don't benefit from this FPN in terms of performance.
|
||||
neck=dict(
|
||||
type='FPN_vitdet',
|
||||
in_channels=[112, 224, 448, 896],
|
||||
out_channels=256,
|
||||
norm_cfg=dict(type='LN', requires_grad=True),
|
||||
use_residual=True,
|
||||
num_outs=5),
|
||||
)
|
||||
# By default, models are trained on 8 GPUs with 2 images per GPU
|
||||
data = dict(samples_per_gpu=2)
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=33, layer_decay_rate=1.0,
|
||||
depths=[4, 4, 21, 4]))
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# fp16 = dict(loss_scale=dict(init_scale=512))
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
|
||||
# Bbox
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.5005
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.717
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.543
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.322
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.540
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.652
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.617
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.617
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.617
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.433
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.658
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.774
|
||||
|
||||
# Segm
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.445
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.687
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.478
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.244
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.477
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.637
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.556
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.556
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.556
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.375
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.595
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.720
|
||||
@@ -0,0 +1,125 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/models/mask_rcnn_r50_fpn.py',
|
||||
'../_base_/datasets/coco_instance.py',
|
||||
'../_base_/schedules/schedule_3x.py',
|
||||
'../_base_/default_runtime.py'
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_b_1k_224.pth'
|
||||
model = dict(
|
||||
backbone=dict(
|
||||
_delete_=True,
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=112,
|
||||
depths=[4, 4, 21, 4],
|
||||
groups=[7, 14, 28, 56],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.4,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=0.5,
|
||||
post_norm=True,
|
||||
with_cp=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
# We leverage the FPN implemented in ViTDet for stable training,
|
||||
# and we don't benefit from this FPN in terms of performance.
|
||||
neck=dict(
|
||||
type='FPN_vitdet',
|
||||
in_channels=[112, 224, 448, 896],
|
||||
out_channels=256,
|
||||
norm_cfg=dict(type='LN', requires_grad=True),
|
||||
use_residual=True,
|
||||
num_outs=5))
|
||||
# By default, models are trained on 8 GPUs with 2 images per GPU
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True, with_mask=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(type='AutoAugment',
|
||||
policies=[
|
||||
[
|
||||
dict(type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),
|
||||
(608, 1333), (640, 1333), (672, 1333), (704, 1333),
|
||||
(736, 1333), (768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True)
|
||||
],
|
||||
[
|
||||
dict(type='Resize',
|
||||
img_scale=[(400, 1333), (500, 1333), (600, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True),
|
||||
dict(type='RandomCrop',
|
||||
crop_type='absolute_range',
|
||||
crop_size=(384, 600),
|
||||
allow_negative_crop=True),
|
||||
dict(type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
override=True,
|
||||
keep_ratio=True)
|
||||
]
|
||||
]),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
||||
]
|
||||
# we use 4 nodes to train this model, with a total batch size of 64
|
||||
data = dict(
|
||||
samples_per_gpu=4,
|
||||
train=dict(pipeline=train_pipeline))
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001 * 2, weight_decay=0.05,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=33, layer_decay_rate=0.9,
|
||||
depths=[4, 4, 21, 4], offset_lr_scale=0.01))
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# fp16 = dict(loss_scale=dict(init_scale=512))
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
|
||||
# Bbox
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.506
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.726
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.554
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.361
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.549
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.651
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.622
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.622
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.622
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.476
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.661
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.764
|
||||
|
||||
# Segm
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.454
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.700
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.492
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.270
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.492
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.638
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.565
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.565
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.565
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.408
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.607
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.717
|
||||
@@ -0,0 +1,84 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/models/mask_rcnn_r50_fpn.py',
|
||||
'../_base_/datasets/coco_instance.py',
|
||||
'../_base_/schedules/schedule_1x.py',
|
||||
'../_base_/default_runtime.py'
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_s_1k_224.pth'
|
||||
model = dict(
|
||||
backbone=dict(
|
||||
_delete_=True,
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=80,
|
||||
depths=[4, 4, 21, 4],
|
||||
groups=[5, 10, 20, 40],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.3,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=1.0,
|
||||
post_norm=True,
|
||||
with_cp=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
# We leverage the FPN implemented in ViTDet for stable training,
|
||||
# and we don't benefit from this FPN in terms of performance.
|
||||
neck=dict(
|
||||
type='FPN_vitdet',
|
||||
in_channels=[80, 160, 320, 640],
|
||||
out_channels=256,
|
||||
norm_cfg=dict(type='LN', requires_grad=True),
|
||||
use_residual=True,
|
||||
num_outs=5),
|
||||
)
|
||||
# By default, models are trained on 8 GPUs with 2 images per GPU
|
||||
data = dict(samples_per_gpu=2)
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=33, layer_decay_rate=1.0,
|
||||
depths=[4, 4, 21, 4]))
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# fp16 = dict(loss_scale=dict(init_scale=512))
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
|
||||
# BBox
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.492
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.707
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.539
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.328
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.531
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.647
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.609
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.609
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.609
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.431
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.650
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.768
|
||||
|
||||
|
||||
# Segm
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.440
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.678
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.476
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.245
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.470
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.633
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.551
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.551
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.551
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.372
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.591
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.714
|
||||
@@ -0,0 +1,126 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/models/mask_rcnn_r50_fpn.py',
|
||||
'../_base_/datasets/coco_instance.py',
|
||||
'../_base_/schedules/schedule_3x.py',
|
||||
'../_base_/default_runtime.py'
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_s_1k_224.pth'
|
||||
model = dict(
|
||||
backbone=dict(
|
||||
_delete_=True,
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=80,
|
||||
depths=[4, 4, 21, 4],
|
||||
groups=[5, 10, 20, 40],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.4,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=1.0,
|
||||
post_norm=True,
|
||||
with_cp=True,
|
||||
dw_kernel_size=3,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
# We leverage the FPN implemented in ViTDet for stable training,
|
||||
# and we don't benefit from this FPN in terms of performance.
|
||||
neck=dict(
|
||||
type='FPN_vitdet',
|
||||
in_channels=[80, 160, 320, 640],
|
||||
out_channels=256,
|
||||
norm_cfg=dict(type='LN', requires_grad=True),
|
||||
use_residual=True,
|
||||
num_outs=5))
|
||||
# By default, models are trained on 8 GPUs with 2 images per GPU
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True, with_mask=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(type='AutoAugment',
|
||||
policies=[
|
||||
[
|
||||
dict(type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),
|
||||
(608, 1333), (640, 1333), (672, 1333), (704, 1333),
|
||||
(736, 1333), (768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True)
|
||||
],
|
||||
[
|
||||
dict(type='Resize',
|
||||
img_scale=[(400, 1333), (500, 1333), (600, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True),
|
||||
dict(type='RandomCrop',
|
||||
crop_type='absolute_range',
|
||||
crop_size=(384, 600),
|
||||
allow_negative_crop=True),
|
||||
dict(type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
override=True,
|
||||
keep_ratio=True)
|
||||
]
|
||||
]),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
||||
]
|
||||
# we use 4 nodes to train this model, with a total batch size of 64
|
||||
data = dict(
|
||||
samples_per_gpu=8,
|
||||
train=dict(pipeline=train_pipeline))
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001 * 2, weight_decay=0.05,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=33, layer_decay_rate=1.0,
|
||||
depths=[4, 4, 21, 4]))
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# fp16 = dict(loss_scale=dict(init_scale=512))
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
|
||||
|
||||
# BBox
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.505
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.720
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.552
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.341
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.545
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.647
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.623
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.623
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.623
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.461
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.657
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.769
|
||||
|
||||
# Segm
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.449
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.690
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.484
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.252
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.480
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.635
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.560
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.560
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.560
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.396
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.596
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.715
|
||||
@@ -0,0 +1,81 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/models/mask_rcnn_r50_fpn.py',
|
||||
'../_base_/datasets/coco_instance.py',
|
||||
'../_base_/schedules/schedule_1x.py',
|
||||
'../_base_/default_runtime.py'
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_t_1k_224.pth'
|
||||
model = dict(
|
||||
backbone=dict(
|
||||
_delete_=True,
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=64,
|
||||
depths=[4, 4, 18, 4],
|
||||
groups=[4, 8, 16, 32],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.2,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=1.0,
|
||||
post_norm=False,
|
||||
with_cp=True,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
# We leverage the FPN implemented in ViTDet for stable training,
|
||||
# and we don't benefit from this FPN in terms of performance.
|
||||
neck=dict(
|
||||
type='FPN_vitdet',
|
||||
in_channels=[64, 128, 256, 512],
|
||||
out_channels=256,
|
||||
norm_cfg=dict(type='LN', requires_grad=True),
|
||||
use_residual=True,
|
||||
num_outs=5)
|
||||
)
|
||||
|
||||
# By default, models are trained on 8 GPUs with 2 images per GPU
|
||||
data = dict(samples_per_gpu=2)
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=30, layer_decay_rate=1.0,
|
||||
depths=[4, 4, 18, 4]))
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# fp16 = dict(loss_scale=dict(init_scale=512))
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
|
||||
# BBox
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.480
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.695
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.528
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.303
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.515
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.629
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.599
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.599
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.599
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.408
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.637
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.750
|
||||
|
||||
# Segm
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.431
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.667 Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.463 Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.225
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.461
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.622
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.543
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.543
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.543
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.352
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.581
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.705
|
||||
@@ -0,0 +1,124 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
_base_ = [
|
||||
'../_base_/models/mask_rcnn_r50_fpn.py',
|
||||
'../_base_/datasets/coco_instance.py',
|
||||
'../_base_/schedules/schedule_3x.py',
|
||||
'../_base_/default_runtime.py'
|
||||
]
|
||||
pretrained = 'https://huggingface.co/OpenGVLab/DCNv4/resolve/main/flash_intern_image_t_1k_224.pth'
|
||||
model = dict(
|
||||
backbone=dict(
|
||||
_delete_=True,
|
||||
type='FlashInternImage',
|
||||
core_op='DCNv4',
|
||||
channels=64,
|
||||
depths=[4, 4, 18, 4],
|
||||
groups=[4, 8, 16, 32],
|
||||
mlp_ratio=4.,
|
||||
drop_path_rate=0.2,
|
||||
norm_layer='LN',
|
||||
layer_scale=1.0,
|
||||
offset_scale=1.0,
|
||||
post_norm=False,
|
||||
with_cp=True,
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=dict(type='Pretrained', checkpoint=pretrained)),
|
||||
# We leverage the FPN implemented in ViTDet for stable training,
|
||||
# and we don't benefit from this FPN in terms of performance.
|
||||
neck=dict(
|
||||
type='FPN_vitdet',
|
||||
in_channels=[64, 128, 256, 512],
|
||||
out_channels=256,
|
||||
norm_cfg=dict(type='LN', requires_grad=True),
|
||||
use_residual=True,
|
||||
num_outs=5),)
|
||||
# By default, models are trained on 8 GPUs with 2 images per GPU
|
||||
img_norm_cfg = dict(
|
||||
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||
train_pipeline = [
|
||||
dict(type='LoadImageFromFile'),
|
||||
dict(type='LoadAnnotations', with_bbox=True, with_mask=True),
|
||||
dict(type='RandomFlip', flip_ratio=0.5),
|
||||
dict(type='AutoAugment',
|
||||
policies=[
|
||||
[
|
||||
dict(type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),
|
||||
(608, 1333), (640, 1333), (672, 1333), (704, 1333),
|
||||
(736, 1333), (768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True)
|
||||
],
|
||||
[
|
||||
dict(type='Resize',
|
||||
img_scale=[(400, 1333), (500, 1333), (600, 1333)],
|
||||
multiscale_mode='value',
|
||||
keep_ratio=True),
|
||||
dict(type='RandomCrop',
|
||||
crop_type='absolute_range',
|
||||
crop_size=(384, 600),
|
||||
allow_negative_crop=True),
|
||||
dict(type='Resize',
|
||||
img_scale=[(480, 1333), (512, 1333), (544, 1333),
|
||||
(576, 1333), (608, 1333), (640, 1333),
|
||||
(672, 1333), (704, 1333), (736, 1333),
|
||||
(768, 1333), (800, 1333)],
|
||||
multiscale_mode='value',
|
||||
override=True,
|
||||
keep_ratio=True)
|
||||
]
|
||||
]),
|
||||
dict(type='Normalize', **img_norm_cfg),
|
||||
dict(type='Pad', size_divisor=32),
|
||||
dict(type='DefaultFormatBundle'),
|
||||
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
||||
]
|
||||
# we use 4 nodes to train this model, with a total batch size of 64
|
||||
data = dict(
|
||||
samples_per_gpu=2,
|
||||
train=dict(pipeline=train_pipeline))
|
||||
optimizer = dict(
|
||||
_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05,
|
||||
constructor='CustomLayerDecayOptimizerConstructor',
|
||||
paramwise_cfg=dict(num_layers=30, layer_decay_rate=1.0,
|
||||
depths=[4, 4, 18, 4]))
|
||||
optimizer_config = dict(grad_clip=None)
|
||||
# fp16 = dict(loss_scale=dict(init_scale=512))
|
||||
evaluation = dict(save_best='auto')
|
||||
checkpoint_config = dict(
|
||||
interval=1,
|
||||
max_keep_ckpts=1,
|
||||
save_last=True,
|
||||
)
|
||||
|
||||
# BBox
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.495
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.707
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.543
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.339
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.532
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.641
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.607
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.607
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.607
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.443
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.643
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.752
|
||||
|
||||
# Segm
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.440
|
||||
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.677
|
||||
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.474
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.255
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.473
|
||||
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.624
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.545
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=300 ] = 0.545
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.545
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.380
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.582
|
||||
# Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.704
|
||||
9
detection/dist_test.sh
Executable file
9
detection/dist_test.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CONFIG=$1
|
||||
CHECKPOINT=$2
|
||||
GPUS=$3
|
||||
PORT=${PORT:-29511}
|
||||
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
|
||||
python -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=$PORT \
|
||||
$(dirname "$0")/test.py $CONFIG $CHECKPOINT --launcher pytorch ${@:4}
|
||||
8
detection/dist_train.sh
Executable file
8
detection/dist_train.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
CONFIG=$1
|
||||
GPUS=$2
|
||||
PORT=${PORT:-29500}
|
||||
# cat /proc/193481/cmdline
|
||||
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
|
||||
python -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=63667 \
|
||||
$(dirname "$0")/train.py $CONFIG --launcher pytorch ${@:3}
|
||||
120
detection/get_flops.py
Normal file
120
detection/get_flops.py
Normal file
@@ -0,0 +1,120 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import argparse
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
from mmcv import Config, DictAction
|
||||
|
||||
from mmdet.models import build_detector
|
||||
import mmcv_custom # noqa: F401,F403
|
||||
import mmdet_custom # noqa: F401,F403
|
||||
|
||||
try:
|
||||
from mmcv.cnn.utils.flops_counter import flops_to_string, params_to_string
|
||||
from mmcv.cnn import get_model_complexity_info
|
||||
except ImportError:
|
||||
raise ImportError('Please upgrade mmcv to >0.6.2')
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Train a detector')
|
||||
parser.add_argument('config', help='train config file path')
|
||||
parser.add_argument(
|
||||
'--shape',
|
||||
type=int,
|
||||
nargs='+',
|
||||
default=[800, 1280],
|
||||
help='input image size')
|
||||
parser.add_argument(
|
||||
'--cfg-options',
|
||||
nargs='+',
|
||||
action=DictAction,
|
||||
help='override some settings in the used config, the key-value pair '
|
||||
'in xxx=yyy format will be merged into config file. If the value to '
|
||||
'be overwritten is a list, it should be like key="[a,b]" or key=a,b '
|
||||
'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" '
|
||||
'Note that the quotation marks are necessary and that no white space '
|
||||
'is allowed.')
|
||||
parser.add_argument(
|
||||
'--size-divisor',
|
||||
type=int,
|
||||
default=32,
|
||||
help='Pad the input image, the minimum size that is divisible '
|
||||
'by size_divisor, -1 means do not pad the image.')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def dcnv3_flops(n, k, c):
|
||||
return 5 * n * k * c
|
||||
|
||||
|
||||
def get_flops(model, input_shape):
|
||||
flops, params = get_model_complexity_info(model, input_shape, as_strings=False)
|
||||
|
||||
backbone = model.backbone
|
||||
backbone_name = type(backbone).__name__
|
||||
_, H, W = input_shape
|
||||
|
||||
temp = 0
|
||||
if 'InternImage' in backbone_name:
|
||||
depths = backbone.depths # [4, 4, 18, 4]
|
||||
for idx, depth in enumerate(depths):
|
||||
channels = backbone.channels * (2 ** idx)
|
||||
h = H / (4 * (2 ** idx))
|
||||
w = W / (4 * (2 ** idx))
|
||||
temp += depth * dcnv3_flops(n=h * w, k=3 * 3, c=channels)
|
||||
|
||||
flops = flops + temp
|
||||
return flops_to_string(flops), params_to_string(params)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
args = parse_args()
|
||||
|
||||
if len(args.shape) == 1:
|
||||
h = w = args.shape[0]
|
||||
elif len(args.shape) == 2:
|
||||
h, w = args.shape
|
||||
else:
|
||||
raise ValueError('invalid input shape')
|
||||
orig_shape = (3, h, w)
|
||||
divisor = args.size_divisor
|
||||
if divisor > 0:
|
||||
h = int(np.ceil(h / divisor)) * divisor
|
||||
w = int(np.ceil(w / divisor)) * divisor
|
||||
|
||||
input_shape = (3, h, w)
|
||||
|
||||
cfg = Config.fromfile(args.config)
|
||||
if args.cfg_options is not None:
|
||||
cfg.merge_from_dict(args.cfg_options)
|
||||
|
||||
model = build_detector(
|
||||
cfg.model,
|
||||
train_cfg=cfg.get('train_cfg'),
|
||||
test_cfg=cfg.get('test_cfg'))
|
||||
|
||||
if torch.cuda.is_available():
|
||||
model.cuda()
|
||||
model.eval()
|
||||
if hasattr(model, 'forward_dummy'):
|
||||
model.forward = model.forward_dummy
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
'FLOPs counter is currently not currently supported with {}'.
|
||||
format(model.__class__.__name__))
|
||||
|
||||
flops, params = get_flops(model, input_shape)
|
||||
split_line = '=' * 30
|
||||
|
||||
if divisor > 0 and \
|
||||
input_shape != orig_shape:
|
||||
print(f'{split_line}\nUse size divisor set input shape '
|
||||
f'from {orig_shape} to {input_shape}\n')
|
||||
print(f'{split_line}\nInput shape: {input_shape}\n'
|
||||
f'Flops: {flops}\nParams: {params}\n{split_line}')
|
||||
print('!!!Please be cautious if you use the results in papers. '
|
||||
'You may need to check if all ops are supported and verify that the '
|
||||
'flops computation is correct.')
|
||||
61
detection/image_demo.py
Normal file
61
detection/image_demo.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import asyncio
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from mmdet.apis import (async_inference_detector, inference_detector,
|
||||
init_detector, show_result_pyplot)
|
||||
import mmcv
|
||||
import mmcv_custom # noqa: F401,F403
|
||||
import mmdet_custom # noqa: F401,F403
|
||||
import os.path as osp
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('img', help='Image file')
|
||||
parser.add_argument('config', help='Config file')
|
||||
parser.add_argument('checkpoint', help='Checkpoint file')
|
||||
parser.add_argument('--out', type=str, default="demo", help='out dir')
|
||||
parser.add_argument(
|
||||
'--device', default='cuda:0', help='Device used for inference')
|
||||
parser.add_argument(
|
||||
'--palette',
|
||||
default='coco',
|
||||
choices=['coco', 'voc', 'citys', 'random'],
|
||||
help='Color palette used for visualization')
|
||||
parser.add_argument(
|
||||
'--score-thr', type=float, default=0.3, help='bbox score threshold')
|
||||
parser.add_argument(
|
||||
'--async-test',
|
||||
action='store_true',
|
||||
help='whether to set async options for async inference.')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main(args):
|
||||
# build the model from a config file and a checkpoint file
|
||||
model = init_detector(args.config, args.checkpoint, device=args.device)
|
||||
# test a single image
|
||||
result = inference_detector(model, args.img)
|
||||
|
||||
mmcv.mkdir_or_exist(args.out)
|
||||
out_file = osp.join(args.out, osp.basename(args.img))
|
||||
# show the results
|
||||
model.show_result(
|
||||
args.img,
|
||||
result,
|
||||
score_thr=args.score_thr,
|
||||
show=False,
|
||||
bbox_color=args.palette,
|
||||
text_color=(200, 200, 200),
|
||||
mask_color=args.palette,
|
||||
out_file=out_file
|
||||
)
|
||||
print(f"Result is save at {out_file}")
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parse_args()
|
||||
main(args)
|
||||
9
detection/mmcv_custom/__init__.py
Normal file
9
detection/mmcv_custom/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
from .custom_layer_decay_optimizer_constructor import CustomLayerDecayOptimizerConstructor
|
||||
__all__ = ['CustomLayerDecayOptimizerConstructor']
|
||||
@@ -0,0 +1,161 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
"""
|
||||
Mostly copy-paste from BEiT library:
|
||||
https://github.com/microsoft/unilm/blob/master/beit/semantic_segmentation/mmcv_custom/layer_decay_optimizer_constructor.py
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from mmcv.runner import OPTIMIZER_BUILDERS, DefaultOptimizerConstructor
|
||||
from mmcv.runner import get_dist_info
|
||||
from mmdet.utils import get_root_logger
|
||||
|
||||
|
||||
def get_num_layer_for_swin(var_name, num_max_layer, depths):
|
||||
if var_name.startswith("backbone.patch_embed"):
|
||||
return 0
|
||||
elif "level_embeds" in var_name:
|
||||
return 0
|
||||
elif var_name.startswith("backbone.layers") or var_name.startswith(
|
||||
"backbone.levels"):
|
||||
if var_name.split('.')[3] not in ['downsample', 'norm']:
|
||||
stage_id = int(var_name.split('.')[2])
|
||||
layer_id = int(var_name.split('.')[4])
|
||||
# layers for Swin-Large: [2, 2, 18, 2]
|
||||
if stage_id == 0:
|
||||
return layer_id + 1
|
||||
elif stage_id == 1:
|
||||
return layer_id + 1 + depths[0]
|
||||
elif stage_id == 2:
|
||||
return layer_id + 1 + depths[0] + depths[1]
|
||||
else:
|
||||
return layer_id + 1 + depths[0] + depths[1] + depths[2]
|
||||
else:
|
||||
stage_id = int(var_name.split('.')[2])
|
||||
if stage_id == 0:
|
||||
return 1 + depths[0]
|
||||
elif stage_id == 1:
|
||||
return 1 + depths[0] + depths[1]
|
||||
elif stage_id == 2:
|
||||
return 1 + depths[0] + depths[1] + depths[2]
|
||||
else:
|
||||
return 1 + depths[0] + depths[1] + depths[2]
|
||||
else:
|
||||
return num_max_layer - 1
|
||||
|
||||
|
||||
@OPTIMIZER_BUILDERS.register_module()
|
||||
class CustomLayerDecayOptimizerConstructor(DefaultOptimizerConstructor):
|
||||
|
||||
def add_params(self, params, module, prefix='', is_dcn_module=None):
|
||||
"""Add all parameters of module to the params list.
|
||||
The parameters of the given module will be added to the list of param
|
||||
groups, with specific rules defined by paramwise_cfg.
|
||||
Args:
|
||||
params (list[dict]): A list of param groups, it will be modified
|
||||
in place.
|
||||
module (nn.Module): The module to be added.
|
||||
prefix (str): The prefix of the module
|
||||
is_dcn_module (int|float|None): If the current module is a
|
||||
submodule of DCN, `is_dcn_module` will be passed to
|
||||
control conv_offset layer's learning rate. Defaults to None.
|
||||
"""
|
||||
parameter_groups = {}
|
||||
logger = get_root_logger()
|
||||
logger.info(self.paramwise_cfg)
|
||||
backbone_small_lr = self.paramwise_cfg.get('backbone_small_lr', False)
|
||||
dino_head = self.paramwise_cfg.get('dino_head', False)
|
||||
num_layers = self.paramwise_cfg.get('num_layers') + 2
|
||||
layer_decay_rate = self.paramwise_cfg.get('layer_decay_rate')
|
||||
depths = self.paramwise_cfg.get('depths')
|
||||
offset_lr_scale = self.paramwise_cfg.get('offset_lr_scale', 1.0)
|
||||
|
||||
logger.info("Build CustomLayerDecayOptimizerConstructor %f - %d" %
|
||||
(layer_decay_rate, num_layers))
|
||||
weight_decay = self.base_wd
|
||||
|
||||
custom_keys = self.paramwise_cfg.get('custom_keys', {})
|
||||
# first sort with alphabet order and then sort with reversed len of str
|
||||
sorted_keys = sorted(custom_keys.keys())
|
||||
for name, param in module.named_parameters():
|
||||
if not param.requires_grad:
|
||||
continue # frozen weights
|
||||
if len(param.shape) == 1 or name.endswith(".bias") or \
|
||||
"relative_position" in name or \
|
||||
"norm" in name or\
|
||||
"sampling_offsets" in name:
|
||||
group_name = "no_decay"
|
||||
this_weight_decay = 0.
|
||||
else:
|
||||
group_name = "decay"
|
||||
this_weight_decay = weight_decay
|
||||
|
||||
layer_id = get_num_layer_for_swin(name, num_layers, depths)
|
||||
if layer_id == num_layers - 1 and dino_head and \
|
||||
("sampling_offsets" in name or "reference_points" in name):
|
||||
group_name = "layer_%d_%s_0.1x" % (layer_id, group_name)
|
||||
elif "sampling_offsets" in name or "reference_points" in name:
|
||||
group_name = "layer_%d_%s_offset_lr_scale" % (layer_id,
|
||||
group_name)
|
||||
elif "offset_mask" in name and "offset_mask_dw" not in name:
|
||||
group_name = "layer_%d_%s_offset_lr_scale" % (layer_id,
|
||||
group_name)
|
||||
elif name.endswith('offset'):
|
||||
group_name = "layer_%d_%s_offset_lr_scale" % (layer_id,
|
||||
group_name)
|
||||
else:
|
||||
group_name = "layer_%d_%s" % (layer_id, group_name)
|
||||
|
||||
# if the parameter match one of the custom keys, ignore other rules
|
||||
this_lr_multi = 1.
|
||||
for key in sorted_keys:
|
||||
if key in f'{name}':
|
||||
logger.info(custom_keys[key])
|
||||
lr_mult = custom_keys[key].get('lr_mult', 1.)
|
||||
this_lr_multi = lr_mult
|
||||
group_name = "%s_%s" % (group_name, key)
|
||||
break
|
||||
if group_name not in parameter_groups:
|
||||
scale = layer_decay_rate ** (num_layers - layer_id - 1)
|
||||
if scale < 1 and backbone_small_lr == True:
|
||||
scale = scale * 0.1
|
||||
if "0.1x" in group_name:
|
||||
scale = scale * 0.1
|
||||
if "offset_lr_scale" in group_name:
|
||||
scale = scale * offset_lr_scale
|
||||
|
||||
parameter_groups[group_name] = {
|
||||
"weight_decay": this_weight_decay,
|
||||
"params": [],
|
||||
"param_names": [],
|
||||
"lr_scale": scale,
|
||||
"group_name": group_name,
|
||||
"lr": scale * self.base_lr * this_lr_multi,
|
||||
}
|
||||
|
||||
parameter_groups[group_name]["params"].append(param)
|
||||
parameter_groups[group_name]["param_names"].append(name)
|
||||
rank, _ = get_dist_info()
|
||||
if rank == 0:
|
||||
to_display = {}
|
||||
for key in parameter_groups:
|
||||
to_display[key] = {
|
||||
"param_names": parameter_groups[key]["param_names"],
|
||||
"lr_scale": parameter_groups[key]["lr_scale"],
|
||||
"lr": parameter_groups[key]["lr"],
|
||||
"weight_decay": parameter_groups[key]["weight_decay"],
|
||||
}
|
||||
logger.info("Param groups = %s" % json.dumps(to_display, indent=2))
|
||||
|
||||
# state_dict = module.state_dict()
|
||||
# for group_name in parameter_groups:
|
||||
# group = parameter_groups[group_name]
|
||||
# for name in group["param_names"]:
|
||||
# group["params"].append(state_dict[name])
|
||||
|
||||
params.extend(parameter_groups.values())
|
||||
8
detection/mmdet_custom/__init__.py
Normal file
8
detection/mmdet_custom/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from .models import * # noqa: F401,F403
|
||||
from .datasets import *
|
||||
7
detection/mmdet_custom/datasets/__init__.py
Normal file
7
detection/mmdet_custom/datasets/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from .crowd_human import CrowdHumanDataset
|
||||
529
detection/mmdet_custom/datasets/crowd_human.py
Normal file
529
detection/mmdet_custom/datasets/crowd_human.py
Normal file
@@ -0,0 +1,529 @@
|
||||
import itertools
|
||||
import logging
|
||||
import os.path as osp
|
||||
import tempfile
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
|
||||
import mmcv
|
||||
import numpy as np
|
||||
from mmcv.utils import print_log
|
||||
from terminaltables import AsciiTable
|
||||
|
||||
from mmdet.core import eval_recalls
|
||||
from mmdet.datasets.api_wrappers import COCO, COCOeval
|
||||
|
||||
from mmdet.datasets.custom import CustomDataset
|
||||
from mmdet.datasets.builder import DATASETS
|
||||
|
||||
|
||||
@DATASETS.register_module()
|
||||
class CrowdHumanDataset(CustomDataset):
|
||||
|
||||
CLASSES = ('person', )
|
||||
|
||||
def load_annotations(self, ann_file):
|
||||
"""Load annotation from COCO style annotation file.
|
||||
Args:
|
||||
ann_file (str): Path of annotation file.
|
||||
Returns:
|
||||
list[dict]: Annotation info from COCO api.
|
||||
"""
|
||||
|
||||
self.coco = COCO(ann_file)
|
||||
# The order of returned `cat_ids` will not
|
||||
# change with the order of the CLASSES
|
||||
self.cat_ids = self.coco.get_cat_ids(cat_names=self.CLASSES)
|
||||
|
||||
self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}
|
||||
self.img_ids = self.coco.get_img_ids()
|
||||
data_infos = []
|
||||
total_ann_ids = []
|
||||
for i in self.img_ids:
|
||||
info = self.coco.load_imgs([i])[0]
|
||||
info['filename'] = info['file_name']
|
||||
data_infos.append(info)
|
||||
ann_ids = self.coco.get_ann_ids(img_ids=[i])
|
||||
total_ann_ids.extend(ann_ids)
|
||||
assert len(set(total_ann_ids)) == len(
|
||||
total_ann_ids), f"Annotation ids in '{ann_file}' are not unique!"
|
||||
return data_infos
|
||||
|
||||
def get_ann_info(self, idx):
|
||||
"""Get COCO annotation by index.
|
||||
Args:
|
||||
idx (int): Index of data.
|
||||
Returns:
|
||||
dict: Annotation info of specified index.
|
||||
"""
|
||||
|
||||
img_id = self.data_infos[idx]['id']
|
||||
ann_ids = self.coco.get_ann_ids(img_ids=[img_id])
|
||||
ann_info = self.coco.load_anns(ann_ids)
|
||||
return self._parse_ann_info(self.data_infos[idx], ann_info)
|
||||
|
||||
def get_cat_ids(self, idx):
|
||||
"""Get COCO category ids by index.
|
||||
Args:
|
||||
idx (int): Index of data.
|
||||
Returns:
|
||||
list[int]: All categories in the image of specified index.
|
||||
"""
|
||||
|
||||
img_id = self.data_infos[idx]['id']
|
||||
ann_ids = self.coco.get_ann_ids(img_ids=[img_id])
|
||||
ann_info = self.coco.load_anns(ann_ids)
|
||||
return [ann['category_id'] for ann in ann_info]
|
||||
|
||||
def _filter_imgs(self, min_size=32):
|
||||
"""Filter images too small or without ground truths."""
|
||||
valid_inds = []
|
||||
# obtain images that contain annotation
|
||||
ids_with_ann = set(_['image_id'] for _ in self.coco.anns.values())
|
||||
# obtain images that contain annotations of the required categories
|
||||
ids_in_cat = set()
|
||||
for i, class_id in enumerate(self.cat_ids):
|
||||
ids_in_cat |= set(self.coco.cat_img_map[class_id])
|
||||
# merge the image id sets of the two conditions and use the merged set
|
||||
# to filter out images if self.filter_empty_gt=True
|
||||
ids_in_cat &= ids_with_ann
|
||||
|
||||
valid_img_ids = []
|
||||
for i, img_info in enumerate(self.data_infos):
|
||||
img_id = self.img_ids[i]
|
||||
if self.filter_empty_gt and img_id not in ids_in_cat:
|
||||
continue
|
||||
if min(img_info['width'], img_info['height']) >= min_size:
|
||||
valid_inds.append(i)
|
||||
valid_img_ids.append(img_id)
|
||||
self.img_ids = valid_img_ids
|
||||
return valid_inds
|
||||
|
||||
def _parse_ann_info(self, img_info, ann_info):
|
||||
"""Parse bbox and mask annotation.
|
||||
Args:
|
||||
ann_info (list[dict]): Annotation info of an image.
|
||||
with_mask (bool): Whether to parse mask annotations.
|
||||
Returns:
|
||||
dict: A dict containing the following keys: bboxes, bboxes_ignore,\
|
||||
labels, masks, seg_map. "masks" are raw annotations and not \
|
||||
decoded into binary masks.
|
||||
"""
|
||||
gt_bboxes = []
|
||||
gt_labels = []
|
||||
gt_bboxes_ignore = []
|
||||
gt_masks_ann = []
|
||||
for i, ann in enumerate(ann_info):
|
||||
if ann.get('ignore', False):
|
||||
continue
|
||||
x1, y1, w, h = ann['bbox']
|
||||
inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))
|
||||
inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))
|
||||
if inter_w * inter_h == 0:
|
||||
continue
|
||||
if ann['area'] <= 0 or w < 1 or h < 1:
|
||||
continue
|
||||
if ann['category_id'] not in self.cat_ids:
|
||||
continue
|
||||
bbox = [x1, y1, x1 + w, y1 + h]
|
||||
if ann.get('iscrowd', False):
|
||||
gt_bboxes_ignore.append(bbox)
|
||||
else:
|
||||
gt_bboxes.append(bbox)
|
||||
gt_labels.append(self.cat2label[ann['category_id']])
|
||||
gt_masks_ann.append(ann.get('segmentation', None))
|
||||
|
||||
if gt_bboxes:
|
||||
gt_bboxes = np.array(gt_bboxes, dtype=np.float32)
|
||||
gt_labels = np.array(gt_labels, dtype=np.int64)
|
||||
else:
|
||||
gt_bboxes = np.zeros((0, 4), dtype=np.float32)
|
||||
gt_labels = np.array([], dtype=np.int64)
|
||||
|
||||
if gt_bboxes_ignore:
|
||||
gt_bboxes_ignore = np.array(gt_bboxes_ignore, dtype=np.float32)
|
||||
else:
|
||||
gt_bboxes_ignore = np.zeros((0, 4), dtype=np.float32)
|
||||
|
||||
seg_map = img_info['filename'].replace('jpg', 'png')
|
||||
|
||||
ann = dict(
|
||||
bboxes=gt_bboxes,
|
||||
labels=gt_labels,
|
||||
bboxes_ignore=gt_bboxes_ignore,
|
||||
masks=gt_masks_ann,
|
||||
seg_map=seg_map)
|
||||
|
||||
return ann
|
||||
|
||||
def xyxy2xywh(self, bbox):
|
||||
"""Convert ``xyxy`` style bounding boxes to ``xywh`` style for COCO
|
||||
evaluation.
|
||||
Args:
|
||||
bbox (numpy.ndarray): The bounding boxes, shape (4, ), in
|
||||
``xyxy`` order.
|
||||
Returns:
|
||||
list[float]: The converted bounding boxes, in ``xywh`` order.
|
||||
"""
|
||||
|
||||
_bbox = bbox.tolist()
|
||||
return [
|
||||
_bbox[0],
|
||||
_bbox[1],
|
||||
_bbox[2] - _bbox[0],
|
||||
_bbox[3] - _bbox[1],
|
||||
]
|
||||
|
||||
def _proposal2json(self, results):
|
||||
"""Convert proposal results to COCO json style."""
|
||||
json_results = []
|
||||
for idx in range(len(self)):
|
||||
img_id = self.img_ids[idx]
|
||||
bboxes = results[idx]
|
||||
for i in range(bboxes.shape[0]):
|
||||
data = dict()
|
||||
data['image_id'] = img_id
|
||||
data['bbox'] = self.xyxy2xywh(bboxes[i])
|
||||
data['score'] = float(bboxes[i][4])
|
||||
data['category_id'] = 1
|
||||
json_results.append(data)
|
||||
return json_results
|
||||
|
||||
def _det2json(self, results):
|
||||
"""Convert detection results to COCO json style."""
|
||||
json_results = []
|
||||
for idx in range(len(self)):
|
||||
img_id = self.img_ids[idx]
|
||||
result = results[idx]
|
||||
for label in range(len(result)):
|
||||
bboxes = result[label]
|
||||
for i in range(bboxes.shape[0]):
|
||||
data = dict()
|
||||
data['image_id'] = img_id
|
||||
data['bbox'] = self.xyxy2xywh(bboxes[i])
|
||||
data['score'] = float(bboxes[i][4])
|
||||
data['category_id'] = self.cat_ids[label]
|
||||
json_results.append(data)
|
||||
return json_results
|
||||
|
||||
def _segm2json(self, results):
|
||||
"""Convert instance segmentation results to COCO json style."""
|
||||
bbox_json_results = []
|
||||
segm_json_results = []
|
||||
for idx in range(len(self)):
|
||||
img_id = self.img_ids[idx]
|
||||
det, seg = results[idx]
|
||||
for label in range(len(det)):
|
||||
# bbox results
|
||||
bboxes = det[label]
|
||||
for i in range(bboxes.shape[0]):
|
||||
data = dict()
|
||||
data['image_id'] = img_id
|
||||
data['bbox'] = self.xyxy2xywh(bboxes[i])
|
||||
data['score'] = float(bboxes[i][4])
|
||||
data['category_id'] = self.cat_ids[label]
|
||||
bbox_json_results.append(data)
|
||||
|
||||
# segm results
|
||||
# some detectors use different scores for bbox and mask
|
||||
if isinstance(seg, tuple):
|
||||
segms = seg[0][label]
|
||||
mask_score = seg[1][label]
|
||||
else:
|
||||
segms = seg[label]
|
||||
mask_score = [bbox[4] for bbox in bboxes]
|
||||
for i in range(bboxes.shape[0]):
|
||||
data = dict()
|
||||
data['image_id'] = img_id
|
||||
data['bbox'] = self.xyxy2xywh(bboxes[i])
|
||||
data['score'] = float(mask_score[i])
|
||||
data['category_id'] = self.cat_ids[label]
|
||||
if isinstance(segms[i]['counts'], bytes):
|
||||
segms[i]['counts'] = segms[i]['counts'].decode()
|
||||
data['segmentation'] = segms[i]
|
||||
segm_json_results.append(data)
|
||||
return bbox_json_results, segm_json_results
|
||||
|
||||
def results2json(self, results, outfile_prefix):
|
||||
"""Dump the detection results to a COCO style json file.
|
||||
There are 3 types of results: proposals, bbox predictions, mask
|
||||
predictions, and they have different data types. This method will
|
||||
automatically recognize the type, and dump them to json files.
|
||||
Args:
|
||||
results (list[list | tuple | ndarray]): Testing results of the
|
||||
dataset.
|
||||
outfile_prefix (str): The filename prefix of the json files. If the
|
||||
prefix is "somepath/xxx", the json files will be named
|
||||
"somepath/xxx.bbox.json", "somepath/xxx.segm.json",
|
||||
"somepath/xxx.proposal.json".
|
||||
Returns:
|
||||
dict[str: str]: Possible keys are "bbox", "segm", "proposal", and \
|
||||
values are corresponding filenames.
|
||||
"""
|
||||
result_files = dict()
|
||||
if isinstance(results[0], list):
|
||||
json_results = self._det2json(results)
|
||||
result_files['bbox'] = f'{outfile_prefix}.bbox.json'
|
||||
result_files['proposal'] = f'{outfile_prefix}.bbox.json'
|
||||
mmcv.dump(json_results, result_files['bbox'])
|
||||
elif isinstance(results[0], tuple):
|
||||
json_results = self._segm2json(results)
|
||||
result_files['bbox'] = f'{outfile_prefix}.bbox.json'
|
||||
result_files['proposal'] = f'{outfile_prefix}.bbox.json'
|
||||
result_files['segm'] = f'{outfile_prefix}.segm.json'
|
||||
mmcv.dump(json_results[0], result_files['bbox'])
|
||||
mmcv.dump(json_results[1], result_files['segm'])
|
||||
elif isinstance(results[0], np.ndarray):
|
||||
json_results = self._proposal2json(results)
|
||||
result_files['proposal'] = f'{outfile_prefix}.proposal.json'
|
||||
mmcv.dump(json_results, result_files['proposal'])
|
||||
else:
|
||||
raise TypeError('invalid type of results')
|
||||
return result_files
|
||||
|
||||
def fast_eval_recall(self, results, proposal_nums, iou_thrs, logger=None):
|
||||
gt_bboxes = []
|
||||
for i in range(len(self.img_ids)):
|
||||
ann_ids = self.coco.get_ann_ids(img_ids=self.img_ids[i])
|
||||
ann_info = self.coco.load_anns(ann_ids)
|
||||
if len(ann_info) == 0:
|
||||
gt_bboxes.append(np.zeros((0, 4)))
|
||||
continue
|
||||
bboxes = []
|
||||
for ann in ann_info:
|
||||
if ann.get('ignore', False) or ann['iscrowd']:
|
||||
continue
|
||||
x1, y1, w, h = ann['bbox']
|
||||
bboxes.append([x1, y1, x1 + w, y1 + h])
|
||||
bboxes = np.array(bboxes, dtype=np.float32)
|
||||
if bboxes.shape[0] == 0:
|
||||
bboxes = np.zeros((0, 4))
|
||||
gt_bboxes.append(bboxes)
|
||||
|
||||
recalls = eval_recalls(
|
||||
gt_bboxes, results, proposal_nums, iou_thrs, logger=logger)
|
||||
ar = recalls.mean(axis=1)
|
||||
return ar
|
||||
|
||||
def format_results(self, results, jsonfile_prefix=None, **kwargs):
|
||||
"""Format the results to json (standard format for COCO evaluation).
|
||||
Args:
|
||||
results (list[tuple | numpy.ndarray]): Testing results of the
|
||||
dataset.
|
||||
jsonfile_prefix (str | None): The prefix of json files. It includes
|
||||
the file path and the prefix of filename, e.g., "a/b/prefix".
|
||||
If not specified, a temp file will be created. Default: None.
|
||||
Returns:
|
||||
tuple: (result_files, tmp_dir), result_files is a dict containing \
|
||||
the json filepaths, tmp_dir is the temporal directory created \
|
||||
for saving json files when jsonfile_prefix is not specified.
|
||||
"""
|
||||
assert isinstance(results, list), 'results must be a list'
|
||||
assert len(results) == len(self), (
|
||||
'The length of results is not equal to the dataset len: {} != {}'.
|
||||
format(len(results), len(self)))
|
||||
|
||||
if jsonfile_prefix is None:
|
||||
tmp_dir = tempfile.TemporaryDirectory()
|
||||
jsonfile_prefix = osp.join(tmp_dir.name, 'results')
|
||||
else:
|
||||
tmp_dir = None
|
||||
result_files = self.results2json(results, jsonfile_prefix)
|
||||
return result_files, tmp_dir
|
||||
|
||||
def evaluate(self,
|
||||
results,
|
||||
metric='bbox',
|
||||
logger=None,
|
||||
jsonfile_prefix=None,
|
||||
classwise=False,
|
||||
proposal_nums=(100, 300, 1000),
|
||||
iou_thrs=None,
|
||||
metric_items=None):
|
||||
"""Evaluation in COCO protocol.
|
||||
Args:
|
||||
results (list[list | tuple]): Testing results of the dataset.
|
||||
metric (str | list[str]): Metrics to be evaluated. Options are
|
||||
'bbox', 'segm', 'proposal', 'proposal_fast'.
|
||||
logger (logging.Logger | str | None): Logger used for printing
|
||||
related information during evaluation. Default: None.
|
||||
jsonfile_prefix (str | None): The prefix of json files. It includes
|
||||
the file path and the prefix of filename, e.g., "a/b/prefix".
|
||||
If not specified, a temp file will be created. Default: None.
|
||||
classwise (bool): Whether to evaluating the AP for each class.
|
||||
proposal_nums (Sequence[int]): Proposal number used for evaluating
|
||||
recalls, such as recall@100, recall@1000.
|
||||
Default: (100, 300, 1000).
|
||||
iou_thrs (Sequence[float], optional): IoU threshold used for
|
||||
evaluating recalls/mAPs. If set to a list, the average of all
|
||||
IoUs will also be computed. If not specified, [0.50, 0.55,
|
||||
0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95] will be used.
|
||||
Default: None.
|
||||
metric_items (list[str] | str, optional): Metric items that will
|
||||
be returned. If not specified, ``['AR@100', 'AR@300',
|
||||
'AR@1000', 'AR_s@1000', 'AR_m@1000', 'AR_l@1000' ]`` will be
|
||||
used when ``metric=='proposal'``, ``['mAP', 'mAP_50', 'mAP_75',
|
||||
'mAP_s', 'mAP_m', 'mAP_l']`` will be used when
|
||||
``metric=='bbox' or metric=='segm'``.
|
||||
Returns:
|
||||
dict[str, float]: COCO style evaluation metric.
|
||||
"""
|
||||
|
||||
metrics = metric if isinstance(metric, list) else [metric]
|
||||
allowed_metrics = ['bbox', 'segm', 'proposal', 'proposal_fast']
|
||||
for metric in metrics:
|
||||
if metric not in allowed_metrics:
|
||||
raise KeyError(f'metric {metric} is not supported')
|
||||
if iou_thrs is None:
|
||||
iou_thrs = np.linspace(
|
||||
.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)
|
||||
if metric_items is not None:
|
||||
if not isinstance(metric_items, list):
|
||||
metric_items = [metric_items]
|
||||
|
||||
result_files, tmp_dir = self.format_results(results, jsonfile_prefix)
|
||||
|
||||
eval_results = OrderedDict()
|
||||
cocoGt = self.coco
|
||||
for metric in metrics:
|
||||
msg = f'Evaluating {metric}...'
|
||||
if logger is None:
|
||||
msg = '\n' + msg
|
||||
print_log(msg, logger=logger)
|
||||
|
||||
if metric == 'proposal_fast':
|
||||
ar = self.fast_eval_recall(
|
||||
results, proposal_nums, iou_thrs, logger='silent')
|
||||
log_msg = []
|
||||
for i, num in enumerate(proposal_nums):
|
||||
eval_results[f'AR@{num}'] = ar[i]
|
||||
log_msg.append(f'\nAR@{num}\t{ar[i]:.4f}')
|
||||
log_msg = ''.join(log_msg)
|
||||
print_log(log_msg, logger=logger)
|
||||
continue
|
||||
|
||||
iou_type = 'bbox' if metric == 'proposal' else metric
|
||||
if metric not in result_files:
|
||||
raise KeyError(f'{metric} is not in results')
|
||||
try:
|
||||
predictions = mmcv.load(result_files[metric])
|
||||
if iou_type == 'segm':
|
||||
# Refer to https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/coco.py#L331 # noqa
|
||||
# When evaluating mask AP, if the results contain bbox,
|
||||
# cocoapi will use the box area instead of the mask area
|
||||
# for calculating the instance area. Though the overall AP
|
||||
# is not affected, this leads to different
|
||||
# small/medium/large mask AP results.
|
||||
for x in predictions:
|
||||
x.pop('bbox')
|
||||
warnings.simplefilter('once')
|
||||
warnings.warn(
|
||||
'The key "bbox" is deleted for more accurate mask AP '
|
||||
'of small/medium/large instances since v2.12.0. This '
|
||||
'does not change the overall mAP calculation.',
|
||||
UserWarning)
|
||||
cocoDt = cocoGt.loadRes(predictions)
|
||||
except IndexError:
|
||||
print_log(
|
||||
'The testing results of the whole dataset is empty.',
|
||||
logger=logger,
|
||||
level=logging.ERROR)
|
||||
break
|
||||
|
||||
cocoEval = COCOeval(cocoGt, cocoDt, iou_type)
|
||||
cocoEval.params.catIds = self.cat_ids
|
||||
cocoEval.params.imgIds = self.img_ids
|
||||
cocoEval.params.maxDets = list(proposal_nums)
|
||||
cocoEval.params.iouThrs = iou_thrs
|
||||
# mapping of cocoEval.stats
|
||||
coco_metric_names = {
|
||||
'mAP': 0,
|
||||
'mAP_50': 1,
|
||||
'mAP_75': 2,
|
||||
'mAP_s': 3,
|
||||
'mAP_m': 4,
|
||||
'mAP_l': 5,
|
||||
'AR@100': 6,
|
||||
'AR@300': 7,
|
||||
'AR@1000': 8,
|
||||
'AR_s@1000': 9,
|
||||
'AR_m@1000': 10,
|
||||
'AR_l@1000': 11
|
||||
}
|
||||
if metric_items is not None:
|
||||
for metric_item in metric_items:
|
||||
if metric_item not in coco_metric_names:
|
||||
raise KeyError(
|
||||
f'metric item {metric_item} is not supported')
|
||||
|
||||
if metric == 'proposal':
|
||||
cocoEval.params.useCats = 0
|
||||
cocoEval.evaluate()
|
||||
cocoEval.accumulate()
|
||||
cocoEval.summarize()
|
||||
if metric_items is None:
|
||||
metric_items = [
|
||||
'AR@100', 'AR@300', 'AR@1000', 'AR_s@1000',
|
||||
'AR_m@1000', 'AR_l@1000'
|
||||
]
|
||||
|
||||
for item in metric_items:
|
||||
val = float(
|
||||
f'{cocoEval.stats[coco_metric_names[item]]:.3f}')
|
||||
eval_results[item] = val
|
||||
else:
|
||||
cocoEval.evaluate()
|
||||
cocoEval.accumulate()
|
||||
cocoEval.summarize()
|
||||
if classwise: # Compute per-category AP
|
||||
# Compute per-category AP
|
||||
# from https://github.com/facebookresearch/detectron2/
|
||||
precisions = cocoEval.eval['precision']
|
||||
# precision: (iou, recall, cls, area range, max dets)
|
||||
assert len(self.cat_ids) == precisions.shape[2]
|
||||
|
||||
results_per_category = []
|
||||
for idx, catId in enumerate(self.cat_ids):
|
||||
# area range index 0: all area ranges
|
||||
# max dets index -1: typically 100 per image
|
||||
nm = self.coco.loadCats(catId)[0]
|
||||
precision = precisions[:, :, idx, 0, -1]
|
||||
precision = precision[precision > -1]
|
||||
if precision.size:
|
||||
ap = np.mean(precision)
|
||||
else:
|
||||
ap = float('nan')
|
||||
results_per_category.append(
|
||||
(f'{nm["name"]}', f'{float(ap):0.3f}'))
|
||||
|
||||
num_columns = min(6, len(results_per_category) * 2)
|
||||
results_flatten = list(
|
||||
itertools.chain(*results_per_category))
|
||||
headers = ['category', 'AP'] * (num_columns // 2)
|
||||
results_2d = itertools.zip_longest(*[
|
||||
results_flatten[i::num_columns]
|
||||
for i in range(num_columns)
|
||||
])
|
||||
table_data = [headers]
|
||||
table_data += [result for result in results_2d]
|
||||
table = AsciiTable(table_data)
|
||||
print_log('\n' + table.table, logger=logger)
|
||||
|
||||
if metric_items is None:
|
||||
metric_items = [
|
||||
'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l'
|
||||
]
|
||||
|
||||
for metric_item in metric_items:
|
||||
key = f'{metric}_{metric_item}'
|
||||
val = float(
|
||||
f'{cocoEval.stats[coco_metric_names[metric_item]]:.3f}'
|
||||
)
|
||||
eval_results[key] = val
|
||||
ap = cocoEval.stats[:6]
|
||||
eval_results[f'{metric}_mAP_copypaste'] = (
|
||||
f'{ap[0]:.3f} {ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} '
|
||||
f'{ap[4]:.3f} {ap[5]:.3f}')
|
||||
if tmp_dir is not None:
|
||||
tmp_dir.cleanup()
|
||||
return
|
||||
11
detection/mmdet_custom/models/__init__.py
Normal file
11
detection/mmdet_custom/models/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from .backbones import * # noqa: F401,F403
|
||||
from .dense_heads import * # noqa: F401,F403
|
||||
from .detectors import * # noqa: F401,F403
|
||||
from .utils import * # noqa: F401,F403
|
||||
from .necks.fpn import *
|
||||
8
detection/mmdet_custom/models/backbones/__init__.py
Normal file
8
detection/mmdet_custom/models/backbones/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2023 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
from .flash_intern_image import FlashInternImage
|
||||
|
||||
__all__ = ['FlashInternImage']
|
||||
763
detection/mmdet_custom/models/backbones/flash_intern_image.py
Normal file
763
detection/mmdet_custom/models/backbones/flash_intern_image.py
Normal file
@@ -0,0 +1,763 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from collections import OrderedDict
|
||||
import torch.utils.checkpoint as checkpoint
|
||||
from timm.models.layers import trunc_normal_, DropPath
|
||||
from mmcv.runner import _load_checkpoint
|
||||
from mmcv.cnn import constant_init, trunc_normal_init
|
||||
from mmdet.utils import get_root_logger
|
||||
from mmdet.models.builder import BACKBONES
|
||||
import torch.nn.functional as F
|
||||
import DCNv4
|
||||
|
||||
|
||||
class to_channels_first(nn.Module):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def forward(self, x):
|
||||
return x.permute(0, 3, 1, 2)
|
||||
|
||||
|
||||
class to_channels_last(nn.Module):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def forward(self, x):
|
||||
return x.permute(0, 2, 3, 1)
|
||||
|
||||
|
||||
def build_norm_layer(dim,
|
||||
norm_layer,
|
||||
in_format='channels_last',
|
||||
out_format='channels_last',
|
||||
eps=1e-6):
|
||||
layers = []
|
||||
if norm_layer == 'BN':
|
||||
if in_format == 'channels_last':
|
||||
layers.append(to_channels_first())
|
||||
layers.append(nn.BatchNorm2d(dim))
|
||||
if out_format == 'channels_last':
|
||||
layers.append(to_channels_last())
|
||||
elif norm_layer == 'LN':
|
||||
if in_format == 'channels_first':
|
||||
layers.append(to_channels_last())
|
||||
layers.append(nn.LayerNorm(dim, eps=eps))
|
||||
if out_format == 'channels_first':
|
||||
layers.append(to_channels_first())
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'build_norm_layer does not support {norm_layer}')
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
|
||||
def build_act_layer(act_layer):
|
||||
if act_layer == 'ReLU':
|
||||
return nn.ReLU(inplace=True)
|
||||
elif act_layer == 'SiLU':
|
||||
return nn.SiLU(inplace=True)
|
||||
elif act_layer == 'GELU':
|
||||
return nn.GELU()
|
||||
|
||||
raise NotImplementedError(f'build_act_layer does not support {act_layer}')
|
||||
|
||||
|
||||
class CrossAttention(nn.Module):
|
||||
r""" Cross Attention Module
|
||||
Args:
|
||||
dim (int): Number of input channels.
|
||||
num_heads (int): Number of attention heads. Default: 8
|
||||
qkv_bias (bool, optional): If True, add a learnable bias to q, k, v.
|
||||
Default: False.
|
||||
qk_scale (float | None, optional): Override default qk scale of
|
||||
head_dim ** -0.5 if set. Default: None.
|
||||
attn_drop (float, optional): Dropout ratio of attention weight.
|
||||
Default: 0.0
|
||||
proj_drop (float, optional): Dropout ratio of output. Default: 0.0
|
||||
attn_head_dim (int, optional): Dimension of attention head.
|
||||
out_dim (int, optional): Dimension of output.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
dim,
|
||||
num_heads=8,
|
||||
qkv_bias=False,
|
||||
qk_scale=None,
|
||||
attn_drop=0.,
|
||||
proj_drop=0.,
|
||||
attn_head_dim=None,
|
||||
out_dim=None):
|
||||
super().__init__()
|
||||
if out_dim is None:
|
||||
out_dim = dim
|
||||
self.num_heads = num_heads
|
||||
head_dim = dim // num_heads
|
||||
if attn_head_dim is not None:
|
||||
head_dim = attn_head_dim
|
||||
all_head_dim = head_dim * self.num_heads
|
||||
self.scale = qk_scale or head_dim ** -0.5
|
||||
assert all_head_dim == dim
|
||||
|
||||
self.q = nn.Linear(dim, all_head_dim, bias=False)
|
||||
self.k = nn.Linear(dim, all_head_dim, bias=False)
|
||||
self.v = nn.Linear(dim, all_head_dim, bias=False)
|
||||
|
||||
if qkv_bias:
|
||||
self.q_bias = nn.Parameter(torch.zeros(all_head_dim))
|
||||
self.k_bias = nn.Parameter(torch.zeros(all_head_dim))
|
||||
self.v_bias = nn.Parameter(torch.zeros(all_head_dim))
|
||||
else:
|
||||
self.q_bias = None
|
||||
self.k_bias = None
|
||||
self.v_bias = None
|
||||
|
||||
self.attn_drop = nn.Dropout(attn_drop)
|
||||
self.proj = nn.Linear(all_head_dim, out_dim)
|
||||
self.proj_drop = nn.Dropout(proj_drop)
|
||||
|
||||
def forward(self, x, k=None, v=None):
|
||||
B, N, C = x.shape
|
||||
N_k = k.shape[1]
|
||||
N_v = v.shape[1]
|
||||
|
||||
q_bias, k_bias, v_bias = None, None, None
|
||||
if self.q_bias is not None:
|
||||
q_bias = self.q_bias
|
||||
k_bias = self.k_bias
|
||||
v_bias = self.v_bias
|
||||
|
||||
q = F.linear(input=x, weight=self.q.weight, bias=q_bias)
|
||||
q = q.reshape(B, N, 1, self.num_heads,
|
||||
-1).permute(2, 0, 3, 1,
|
||||
4).squeeze(0) # (B, N_head, N_q, dim)
|
||||
|
||||
k = F.linear(input=k, weight=self.k.weight, bias=k_bias)
|
||||
k = k.reshape(B, N_k, 1, self.num_heads, -1).permute(2, 0, 3, 1,
|
||||
4).squeeze(0)
|
||||
|
||||
v = F.linear(input=v, weight=self.v.weight, bias=v_bias)
|
||||
v = v.reshape(B, N_v, 1, self.num_heads, -1).permute(2, 0, 3, 1,
|
||||
4).squeeze(0)
|
||||
|
||||
q = q * self.scale
|
||||
attn = (q @ k.transpose(-2, -1)) # (B, N_head, N_q, N_k)
|
||||
|
||||
attn = attn.softmax(dim=-1)
|
||||
attn = self.attn_drop(attn)
|
||||
|
||||
x = (attn @ v).transpose(1, 2).reshape(B, N, -1)
|
||||
x = self.proj(x)
|
||||
x = self.proj_drop(x)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class AttentiveBlock(nn.Module):
|
||||
r"""Attentive Block
|
||||
Args:
|
||||
dim (int): Number of input channels.
|
||||
num_heads (int): Number of attention heads. Default: 8
|
||||
qkv_bias (bool, optional): If True, add a learnable bias to q, k, v.
|
||||
Default: False.
|
||||
qk_scale (float | None, optional): Override default qk scale of
|
||||
head_dim ** -0.5 if set. Default: None.
|
||||
drop (float, optional): Dropout rate. Default: 0.0.
|
||||
attn_drop (float, optional): Attention dropout rate. Default: 0.0.
|
||||
drop_path (float | tuple[float], optional): Stochastic depth rate.
|
||||
Default: 0.0.
|
||||
norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm.
|
||||
attn_head_dim (int, optional): Dimension of attention head. Default: None.
|
||||
out_dim (int, optional): Dimension of output. Default: None.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
dim,
|
||||
num_heads,
|
||||
qkv_bias=False,
|
||||
qk_scale=None,
|
||||
drop=0.,
|
||||
attn_drop=0.,
|
||||
drop_path=0.,
|
||||
norm_layer="LN",
|
||||
attn_head_dim=None,
|
||||
out_dim=None):
|
||||
super().__init__()
|
||||
|
||||
self.norm1_q = build_norm_layer(dim, norm_layer, eps=1e-6)
|
||||
self.norm1_k = build_norm_layer(dim, norm_layer, eps=1e-6)
|
||||
self.norm1_v = build_norm_layer(dim, norm_layer, eps=1e-6)
|
||||
self.cross_dcn = CrossAttention(dim,
|
||||
num_heads=num_heads,
|
||||
qkv_bias=qkv_bias,
|
||||
qk_scale=qk_scale,
|
||||
attn_drop=attn_drop,
|
||||
proj_drop=drop,
|
||||
attn_head_dim=attn_head_dim,
|
||||
out_dim=out_dim)
|
||||
|
||||
self.drop_path = DropPath(
|
||||
drop_path) if drop_path > 0. else nn.Identity()
|
||||
|
||||
def forward(self,
|
||||
x_q,
|
||||
x_kv,
|
||||
pos_q,
|
||||
pos_k,
|
||||
bool_masked_pos,
|
||||
rel_pos_bias=None):
|
||||
x_q = self.norm1_q(x_q + pos_q)
|
||||
x_k = self.norm1_k(x_kv + pos_k)
|
||||
x_v = self.norm1_v(x_kv)
|
||||
|
||||
x = self.cross_dcn(x_q, k=x_k, v=x_v)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class AttentionPoolingBlock(AttentiveBlock):
|
||||
|
||||
def forward(self, x):
|
||||
x_q = x.mean(1, keepdim=True)
|
||||
x_kv = x
|
||||
pos_q, pos_k = 0, 0
|
||||
x = super().forward(x_q, x_kv, pos_q, pos_k,
|
||||
bool_masked_pos=None,
|
||||
rel_pos_bias=None)
|
||||
x = x.squeeze(1)
|
||||
return x
|
||||
|
||||
|
||||
class StemLayer(nn.Module):
|
||||
r""" Stem layer of InternImage
|
||||
Args:
|
||||
in_chans (int): number of input channels
|
||||
out_chans (int): number of output channels
|
||||
act_layer (str): activation layer
|
||||
norm_layer (str): normalization layer
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_chans=3,
|
||||
out_chans=96,
|
||||
act_layer='GELU',
|
||||
norm_layer='BN'):
|
||||
super().__init__()
|
||||
self.conv1 = nn.Conv2d(in_chans,
|
||||
out_chans // 2,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1)
|
||||
self.norm1 = build_norm_layer(out_chans // 2, norm_layer,
|
||||
'channels_first', 'channels_first')
|
||||
self.act = build_act_layer(act_layer)
|
||||
self.conv2 = nn.Conv2d(out_chans // 2,
|
||||
out_chans,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1)
|
||||
self.norm2 = build_norm_layer(out_chans, norm_layer, 'channels_first',
|
||||
'channels_last')
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
x = self.norm1(x)
|
||||
x = self.act(x)
|
||||
x = self.conv2(x)
|
||||
x = self.norm2(x)
|
||||
return x
|
||||
|
||||
|
||||
|
||||
class DownsampleLayer(nn.Module):
|
||||
r""" Downsample layer of InternImage
|
||||
Args:
|
||||
channels (int): number of input channels
|
||||
norm_layer (str): normalization layer
|
||||
"""
|
||||
|
||||
def __init__(self, channels, norm_layer='LN'):
|
||||
super().__init__()
|
||||
self.conv = nn.Conv2d(channels,
|
||||
2 * channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
bias=False)
|
||||
self.norm = build_norm_layer(2 * channels, norm_layer,
|
||||
'channels_first', 'channels_first')
|
||||
|
||||
|
||||
def forward(self, x, shape=None):
|
||||
H, W = shape
|
||||
N, HW, C = x.shape
|
||||
|
||||
x = x.view(N, H, W, C)
|
||||
x = self.conv(x.permute(0, 3, 1, 2))
|
||||
x = self.norm(x) # B C H W
|
||||
H, W = x.size(2), x.size(3)
|
||||
x = x.flatten(2).permute(0, 2, 1)
|
||||
|
||||
return x, (H, W)
|
||||
|
||||
|
||||
|
||||
class MLPLayer(nn.Module):
|
||||
r""" MLP layer of InternImage
|
||||
Args:
|
||||
in_features (int): number of input features
|
||||
hidden_features (int): number of hidden features
|
||||
out_features (int): number of output features
|
||||
act_layer (str): activation layer
|
||||
drop (float): dropout rate
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_features,
|
||||
hidden_features=None,
|
||||
out_features=None,
|
||||
act_layer='GELU',
|
||||
mlp_fc2_bias=False,
|
||||
drop=0.):
|
||||
super().__init__()
|
||||
out_features = out_features or in_features
|
||||
hidden_features = hidden_features or in_features
|
||||
self.fc1 = nn.Linear(in_features, hidden_features, bias=True)
|
||||
self.act = build_act_layer(act_layer)
|
||||
self.fc2 = nn.Linear(hidden_features, out_features, bias=mlp_fc2_bias)
|
||||
self.drop = nn.Dropout(drop)
|
||||
|
||||
|
||||
def forward(self, x, shape, level_idx=0):
|
||||
x = self.fc1(x)
|
||||
x = self.act(x)
|
||||
x = self.drop(x)
|
||||
x = self.fc2(x)
|
||||
x = self.drop(x)
|
||||
return x
|
||||
|
||||
|
||||
class InternImageLayer(nn.Module):
|
||||
r""" Basic layer of InternImage
|
||||
Args:
|
||||
core_op (nn.Module): core operation of InternImage
|
||||
channels (int): number of input channels
|
||||
groups (list): Groups of each block.
|
||||
mlp_ratio (float): ratio of mlp hidden features to input channels
|
||||
drop (float): dropout rate
|
||||
drop_path (float): drop path rate
|
||||
act_layer (str): activation layer
|
||||
norm_layer (str): normalization layer
|
||||
post_norm (bool): whether to use post normalization
|
||||
layer_scale (float): layer scale
|
||||
offset_scale (float): offset scale
|
||||
with_cp (bool): whether to use checkpoint
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
core_op,
|
||||
channels,
|
||||
groups,
|
||||
mlp_ratio=4.,
|
||||
drop=0.,
|
||||
drop_path=0.,
|
||||
act_layer='GELU',
|
||||
norm_layer='LN',
|
||||
post_norm=False,
|
||||
layer_scale=None,
|
||||
offset_scale=1.0,
|
||||
with_cp=False,
|
||||
dcn_output_bias=False,
|
||||
mlp_fc2_bias=False,
|
||||
dw_kernel_size=None, # for InternImage-H/G
|
||||
res_post_norm=False, # for InternImage-H/G
|
||||
center_feature_scale=False): # for InternImage-H/G
|
||||
super().__init__()
|
||||
self.channels = channels
|
||||
self.groups = groups
|
||||
self.mlp_ratio = mlp_ratio
|
||||
self.with_cp = with_cp
|
||||
|
||||
self.norm1 = build_norm_layer(channels, 'LN')
|
||||
self.post_norm = post_norm
|
||||
self.dcn = core_op(
|
||||
channels=channels,
|
||||
group=groups,
|
||||
offset_scale=offset_scale,
|
||||
dw_kernel_size=dw_kernel_size,
|
||||
output_bias=dcn_output_bias,
|
||||
)
|
||||
self.drop_path = DropPath(drop_path) if drop_path > 0. \
|
||||
else nn.Identity()
|
||||
self.norm2 = build_norm_layer(channels, 'LN')
|
||||
self.mlp = MLPLayer(in_features=channels,
|
||||
hidden_features=int(channels * mlp_ratio),
|
||||
act_layer=act_layer,
|
||||
drop=drop,
|
||||
mlp_fc2_bias=mlp_fc2_bias
|
||||
)
|
||||
self.layer_scale = layer_scale is not None
|
||||
if self.layer_scale:
|
||||
self.gamma1 = nn.Parameter(layer_scale * torch.ones(channels),
|
||||
requires_grad=True)
|
||||
self.gamma2 = nn.Parameter(layer_scale * torch.ones(channels),
|
||||
requires_grad=True)
|
||||
self.res_post_norm = res_post_norm
|
||||
if res_post_norm:
|
||||
self.res_post_norm1 = build_norm_layer(channels, 'LN')
|
||||
self.res_post_norm2 = build_norm_layer(channels, 'LN')
|
||||
def forward(self, x, shape, level_idx=0):
|
||||
|
||||
def _inner_forward(x, shape, level_idx):
|
||||
if not self.layer_scale:
|
||||
if self.post_norm:
|
||||
x = x + self.drop_path(self.norm1(self.dcn(x, shape, level_idx)))
|
||||
x = x + self.drop_path(self.norm2(self.mlp(x, shape, level_idx)))
|
||||
elif self.res_post_norm: # for InternImage-H/G
|
||||
x = x + self.drop_path(self.res_post_norm1(self.dcn(self.norm1(x), shape, level_idx)))
|
||||
x = x + self.drop_path(self.res_post_norm2(self.mlp(self.norm2(x), shape, level_idx)))
|
||||
|
||||
else:
|
||||
x = x + self.drop_path(self.dcn(self.norm1(x), shape, level_idx))
|
||||
x = x + self.drop_path(self.mlp(self.norm2(x), shape, level_idx))
|
||||
return x
|
||||
if self.post_norm:
|
||||
x = x + self.drop_path(self.gamma1 * self.norm1(self.dcn(x, shape)))
|
||||
x = x + self.drop_path(self.gamma2 * self.norm2(self.mlp(x, shape, level_idx)))
|
||||
else:
|
||||
x = x + self.drop_path(self.gamma1 * self.dcn(self.norm1(x), shape))
|
||||
x = x + self.drop_path(self.gamma2 * self.mlp(self.norm2(x), shape, level_idx))
|
||||
return x
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
x = checkpoint.checkpoint(_inner_forward, x, shape, level_idx)
|
||||
else:
|
||||
x = _inner_forward(x, shape, level_idx)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class InternImageBlock(nn.Module):
|
||||
r""" Block of InternImage
|
||||
Args:
|
||||
core_op (nn.Module): core operation of InternImage
|
||||
channels (int): number of input channels
|
||||
depths (list): Depth of each block.
|
||||
groups (list): Groups of each block.
|
||||
mlp_ratio (float): ratio of mlp hidden features to input channels
|
||||
drop (float): dropout rate
|
||||
drop_path (float): drop path rate
|
||||
act_layer (str): activation layer
|
||||
norm_layer (str): normalization layer
|
||||
post_norm (bool): whether to use post normalization
|
||||
layer_scale (float): layer scale
|
||||
offset_scale (float): offset scale
|
||||
with_cp (bool): whether to use checkpoint
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
core_op,
|
||||
channels,
|
||||
depth,
|
||||
groups,
|
||||
downsample=True,
|
||||
downsample_layer=DownsampleLayer,
|
||||
mlp_ratio=4.,
|
||||
drop=0.,
|
||||
drop_path=0.,
|
||||
act_layer='GELU',
|
||||
norm_layer='LN',
|
||||
post_norm=False,
|
||||
offset_scale=0.5,
|
||||
layer_scale=None,
|
||||
with_cp=False,
|
||||
dcn_output_bias=False,
|
||||
mlp_fc2_bias=False,
|
||||
dw_kernel_size=None, # for InternImage-H/G
|
||||
post_norm_block_ids=None, # for InternImage-H/G
|
||||
res_post_norm=False, # for InternImage-H/G
|
||||
center_feature_scale=False): # for InternImage-H/G
|
||||
super().__init__()
|
||||
self.channels = channels
|
||||
self.depth = depth
|
||||
self.post_norm = post_norm
|
||||
self.center_feature_scale = center_feature_scale
|
||||
|
||||
self.blocks = nn.ModuleList([
|
||||
InternImageLayer(
|
||||
core_op=core_op,
|
||||
channels=channels,
|
||||
groups=groups,
|
||||
mlp_ratio=mlp_ratio,
|
||||
drop=drop,
|
||||
drop_path=drop_path[i] if isinstance(
|
||||
drop_path, list) else drop_path,
|
||||
act_layer=act_layer,
|
||||
norm_layer=norm_layer,
|
||||
post_norm=post_norm,
|
||||
layer_scale=layer_scale,
|
||||
offset_scale=offset_scale,
|
||||
with_cp=with_cp,
|
||||
dcn_output_bias=dcn_output_bias,
|
||||
mlp_fc2_bias=mlp_fc2_bias,
|
||||
dw_kernel_size=dw_kernel_size, # for InternImage-H/G
|
||||
res_post_norm=res_post_norm, # for InternImage-H/G
|
||||
center_feature_scale=center_feature_scale # for InternImage-H/G
|
||||
) for i in range(depth)
|
||||
])
|
||||
if not self.post_norm or center_feature_scale:
|
||||
self.norm = build_norm_layer(channels, 'LN')
|
||||
self.post_norm_block_ids = post_norm_block_ids
|
||||
if post_norm_block_ids is not None: # for InternImage-H/G
|
||||
self.post_norms = nn.ModuleList(
|
||||
[build_norm_layer(channels, 'LN', eps=1e-6) for _ in post_norm_block_ids]
|
||||
)
|
||||
self.downsample = downsample_layer(
|
||||
channels=channels, norm_layer=norm_layer) if downsample else None
|
||||
|
||||
|
||||
def forward(self, x, return_wo_downsample=False, shape=None, level_idx=0
|
||||
):
|
||||
for i, blk in enumerate(self.blocks):
|
||||
x = blk(x, shape=shape, level_idx=level_idx)
|
||||
if (self.post_norm_block_ids is not None) and (i in self.post_norm_block_ids):
|
||||
index = self.post_norm_block_ids.index(i)
|
||||
x = self.post_norms[index](x) # for InternImage-H/G
|
||||
if not self.post_norm or self.center_feature_scale:
|
||||
x = self.norm(x)
|
||||
if return_wo_downsample:
|
||||
x_ = x.clone()
|
||||
if self.downsample is not None:
|
||||
x, shape = self.downsample(x, shape=shape)
|
||||
|
||||
if return_wo_downsample:
|
||||
return x, x_, shape
|
||||
return x, shape
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class FlashInternImage(nn.Module):
|
||||
r""" FlashInternImage
|
||||
A PyTorch impl based on :
|
||||
`InternImage: Exploring Large-Scale Vision Foundation Models with Deformable Convolutions` -
|
||||
https://arxiv.org/pdf/2103.14030
|
||||
'DCNv4': TODO: add arxiv
|
||||
Args:
|
||||
core_op (str): Core operator. Default: 'DCNv4'
|
||||
channels (int): Number of the first stage. Default: 64
|
||||
depths (list): Depth of each block. Default: [3, 4, 18, 5]
|
||||
groups (list): Groups of each block. Default: [3, 6, 12, 24]
|
||||
num_classes (int): Number of classes. Default: 1000
|
||||
mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4.
|
||||
drop_rate (float): Probability of an element to be zeroed. Default: 0.
|
||||
drop_path_rate (float): Stochastic depth rate. Default: 0.2
|
||||
act_layer (str): Activation layer. Default: 'GELU'
|
||||
norm_layer (str): Normalization layer. Default: 'LN'
|
||||
layer_scale (bool): Whether to use layer scale. Default: False
|
||||
cls_scale (bool): Whether to use class scale. Default: False
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
dw_kernel_size (int): Size of the dwconv. Default: None
|
||||
use_clip_projector (bool): Whether to use clip projector. Default: False
|
||||
level2_post_norm (bool): Whether to use level2 post norm. Default: False
|
||||
level2_post_norm_block_ids (list): Indexes of post norm blocks. Default: None
|
||||
res_post_norm (bool): Whether to use res post norm. Default: False
|
||||
center_feature_scale (bool): Whether to use center feature scale. Default: False
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
core_op='DCNv4',
|
||||
channels=64,
|
||||
depths=[3, 4, 18, 5],
|
||||
groups=[3, 6, 12, 24],
|
||||
num_classes=1000,
|
||||
mlp_ratio=4.,
|
||||
drop_rate=0.,
|
||||
drop_path_rate=0.2,
|
||||
drop_path_type='linear',
|
||||
act_layer='GELU',
|
||||
norm_layer='LN',
|
||||
layer_scale=None,
|
||||
offset_scale=0.5,
|
||||
post_norm=False,
|
||||
with_cp=False,
|
||||
mlp_fc2_bias=False,
|
||||
dcn_output_bias=False,
|
||||
dw_kernel_size=None, # for InternImage-H/G
|
||||
level2_post_norm=False, # for InternImage-H/G
|
||||
level2_post_norm_block_ids=None, # for InternImage-H/G
|
||||
res_post_norm=False, # for InternImage-H/G
|
||||
center_feature_scale=False, # for InternImage-H/G
|
||||
out_indices=(0, 1, 2, 3),
|
||||
init_cfg=None,
|
||||
**kwargs):
|
||||
super().__init__()
|
||||
self.core_op = core_op
|
||||
self.num_levels = len(depths)
|
||||
self.depths = depths
|
||||
self.channels = channels
|
||||
self.num_features = int(channels * 2**(self.num_levels - 1))
|
||||
self.post_norm = post_norm
|
||||
self.mlp_ratio = mlp_ratio
|
||||
self.init_cfg = init_cfg
|
||||
self.out_indices = out_indices
|
||||
self.level2_post_norm_block_ids = level2_post_norm_block_ids
|
||||
logger = get_root_logger()
|
||||
logger.info(f'using core type: {core_op}')
|
||||
logger.info(f'using activation layer: {act_layer}')
|
||||
logger.info(f'using main norm layer: {norm_layer}')
|
||||
logger.info(f'using dpr: {drop_path_type}, {drop_path_rate}')
|
||||
logger.info(f"level2_post_norm: {level2_post_norm}")
|
||||
logger.info(f"level2_post_norm_block_ids: {level2_post_norm_block_ids}")
|
||||
logger.info(f"res_post_norm: {res_post_norm}")
|
||||
|
||||
in_chans = 3
|
||||
self.patch_embed = StemLayer(in_chans=in_chans,
|
||||
out_chans=channels,
|
||||
act_layer=act_layer,
|
||||
norm_layer=norm_layer)
|
||||
self.pos_drop = nn.Dropout(p=drop_rate)
|
||||
|
||||
dpr = [
|
||||
x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))
|
||||
]
|
||||
if drop_path_type == 'uniform':
|
||||
for i in range(len(dpr)):
|
||||
dpr[i] = drop_path_rate
|
||||
|
||||
self.levels = nn.ModuleList()
|
||||
for i in range(self.num_levels):
|
||||
post_norm_block_ids = level2_post_norm_block_ids if level2_post_norm and (
|
||||
i == 2) else None # for InternImage-H/G
|
||||
|
||||
level = InternImageBlock(
|
||||
core_op=getattr(DCNv4, core_op),
|
||||
channels=int(channels * 2**i),
|
||||
depth=depths[i],
|
||||
groups=groups[i],
|
||||
mlp_ratio=self.mlp_ratio,
|
||||
drop=drop_rate,
|
||||
drop_path=dpr[sum(depths[:i]):sum(depths[:i + 1])],
|
||||
act_layer=act_layer,
|
||||
norm_layer=norm_layer,
|
||||
post_norm=post_norm,
|
||||
downsample=(i < self.num_levels - 1),
|
||||
downsample_layer = DownsampleLayer,
|
||||
layer_scale=layer_scale,
|
||||
offset_scale=offset_scale,
|
||||
with_cp=with_cp,
|
||||
mlp_fc2_bias=mlp_fc2_bias,
|
||||
dcn_output_bias=dcn_output_bias,
|
||||
dw_kernel_size=dw_kernel_size, # for InternImage-H/G
|
||||
post_norm_block_ids=post_norm_block_ids, # for InternImage-H/G
|
||||
res_post_norm=res_post_norm, # for InternImage-H/G
|
||||
center_feature_scale=center_feature_scale # for InternImage-H/G
|
||||
)
|
||||
self.levels.append(level)
|
||||
|
||||
self.num_layers = len(depths)
|
||||
self.apply(self._init_weights)
|
||||
self.apply(self._init_deform_weights)
|
||||
|
||||
def init_weights(self):
|
||||
logger = get_root_logger()
|
||||
if self.init_cfg is None:
|
||||
logger.warn(f'No pre-trained weights for '
|
||||
f'{self.__class__.__name__}, '
|
||||
f'training start from scratch')
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Linear):
|
||||
trunc_normal_init(m, std=.02, bias=0.)
|
||||
elif isinstance(m, nn.LayerNorm):
|
||||
constant_init(m, 1.0)
|
||||
else:
|
||||
assert 'checkpoint' in self.init_cfg, f'Only support ' \
|
||||
f'specify `Pretrained` in ' \
|
||||
f'`init_cfg` in ' \
|
||||
f'{self.__class__.__name__} '
|
||||
ckpt = _load_checkpoint(self.init_cfg.checkpoint,
|
||||
logger=logger,
|
||||
map_location='cpu')
|
||||
if 'state_dict' in ckpt:
|
||||
_state_dict = ckpt['state_dict']
|
||||
elif 'model_ema' in ckpt:
|
||||
_state_dict = ckpt['model_ema']
|
||||
elif 'model' in ckpt:
|
||||
_state_dict = ckpt['model']
|
||||
else:
|
||||
_state_dict = ckpt
|
||||
|
||||
state_dict = OrderedDict()
|
||||
for k, v in _state_dict.items():
|
||||
if k.startswith('backbone.'):
|
||||
state_dict[k[9:]] = v
|
||||
else:
|
||||
state_dict[k] = v
|
||||
|
||||
# strip prefix of state_dict
|
||||
if list(state_dict.keys())[0].startswith('module.'):
|
||||
state_dict = {k[7:]: v for k, v in state_dict.items()}
|
||||
|
||||
# load state_dict
|
||||
meg = self.load_state_dict(state_dict, False)
|
||||
logger.info(meg)
|
||||
|
||||
def _init_weights(self, m):
|
||||
if isinstance(m, nn.Linear):
|
||||
trunc_normal_(m.weight, std=.02)
|
||||
if isinstance(m, nn.Linear) and m.bias is not None:
|
||||
nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.LayerNorm):
|
||||
nn.init.constant_(m.bias, 0)
|
||||
nn.init.constant_(m.weight, 1.0)
|
||||
|
||||
def _init_deform_weights(self, m):
|
||||
if isinstance(m, getattr(DCNv4, self.core_op)):
|
||||
m._reset_parameters()
|
||||
|
||||
@torch.jit.ignore
|
||||
def lr_decay_keywards(self, decay_ratio=0.87):
|
||||
lr_ratios = {}
|
||||
|
||||
# blocks
|
||||
idx = 0
|
||||
for i in range(4):
|
||||
layer_num = 3 - i # 3 2 1 0
|
||||
for j in range(self.depths[layer_num]):
|
||||
block_num = self.depths[layer_num] - j - 1
|
||||
tag = 'levels.{}.blocks.{}.'.format(layer_num, block_num)
|
||||
decay = 1.0 * (decay_ratio**idx)
|
||||
lr_ratios[tag] = decay
|
||||
idx += 1
|
||||
# patch_embed (before stage-1)
|
||||
lr_ratios["patch_embed"] = lr_ratios['levels.0.blocks.0.']
|
||||
# levels.0.downsample (between stage-1 and stage-2)
|
||||
lr_ratios["levels.0.downsample"] = lr_ratios['levels.1.blocks.0.']
|
||||
lr_ratios["levels.0.norm"] = lr_ratios['levels.1.blocks.0.']
|
||||
# levels.1.downsample (between stage-2 and stage-3)
|
||||
lr_ratios["levels.1.downsample"] = lr_ratios['levels.2.blocks.0.']
|
||||
lr_ratios["levels.1.norm"] = lr_ratios['levels.2.blocks.0.']
|
||||
# levels.2.downsample (between stage-3 and stage-4)
|
||||
lr_ratios["levels.2.downsample"] = lr_ratios['levels.3.blocks.0.']
|
||||
lr_ratios["levels.2.norm"] = lr_ratios['levels.3.blocks.0.']
|
||||
return lr_ratios
|
||||
|
||||
def forward(self, x):
|
||||
x = self.patch_embed(x)
|
||||
N, H, W, C = x.shape
|
||||
x = x.view(N, H*W, C)
|
||||
|
||||
shape=(H, W)
|
||||
seq_out = []
|
||||
for level_idx, level in enumerate(self.levels):
|
||||
old_shape = shape
|
||||
x, x_ , shape = level(x, return_wo_downsample=True, shape=shape, level_idx=level_idx)
|
||||
if level_idx in self.out_indices:
|
||||
h, w= old_shape
|
||||
seq_out.append(x_.reshape(N, h, w, -1).permute(0, 3, 1, 2))
|
||||
return seq_out
|
||||
13
detection/mmdet_custom/models/dense_heads/__init__.py
Normal file
13
detection/mmdet_custom/models/dense_heads/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from .deformable_detr_head import DeformableDETRHead
|
||||
from .detr_head import DETRHead
|
||||
from .dino_head import DINOHead
|
||||
from .msda import FlashMultiScaleDeformableAttention
|
||||
from .bbox_head import DCNv4FCBBoxHead
|
||||
from .mask_rcnn import MaskRCNN_
|
||||
__all__ = ['DeformableDETRHead', 'DETRHead', 'DINOHead']
|
||||
222
detection/mmdet_custom/models/dense_heads/bbox_head.py
Normal file
222
detection/mmdet_custom/models/dense_heads/bbox_head.py
Normal file
@@ -0,0 +1,222 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule
|
||||
|
||||
from mmdet.models.builder import HEADS
|
||||
from mmdet.models.utils import build_linear_layer
|
||||
from mmdet.models.roi_heads.bbox_heads.bbox_head import BBoxHead
|
||||
import DCNv4
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@HEADS.register_module()
|
||||
class DCNv4FCBBoxHead(BBoxHead):
|
||||
r"""More general bbox head, with shared conv and fc layers and two optional
|
||||
separated branches.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
/-> cls convs -> cls fcs -> cls
|
||||
shared convs -> shared fcs
|
||||
\-> reg convs -> reg fcs -> reg
|
||||
""" # noqa: W605
|
||||
|
||||
def __init__(self,
|
||||
num_shared_convs=0,
|
||||
num_shared_fcs=0,
|
||||
num_cls_convs=0,
|
||||
num_cls_fcs=0,
|
||||
num_reg_convs=0,
|
||||
num_reg_fcs=0,
|
||||
conv_out_channels=256,
|
||||
fc_out_channels=1024,
|
||||
conv_cfg=None,
|
||||
norm_cfg=None,
|
||||
init_cfg=None,
|
||||
with_dcn=True,
|
||||
short_cut=False,
|
||||
*args,
|
||||
**kwargs):
|
||||
super(DCNv4FCBBoxHead, self).__init__(
|
||||
*args, init_cfg=init_cfg, **kwargs)
|
||||
assert (num_shared_convs + num_shared_fcs + num_cls_convs +
|
||||
num_cls_fcs + num_reg_convs + num_reg_fcs > 0)
|
||||
if num_cls_convs > 0 or num_reg_convs > 0:
|
||||
assert num_shared_fcs == 0
|
||||
if not self.with_cls:
|
||||
assert num_cls_convs == 0 and num_cls_fcs == 0
|
||||
if not self.with_reg:
|
||||
assert num_reg_convs == 0 and num_reg_fcs == 0
|
||||
self.num_shared_convs = num_shared_convs
|
||||
self.num_shared_fcs = num_shared_fcs
|
||||
self.num_cls_convs = num_cls_convs
|
||||
self.num_cls_fcs = num_cls_fcs
|
||||
self.num_reg_convs = num_reg_convs
|
||||
self.num_reg_fcs = num_reg_fcs
|
||||
self.conv_out_channels = conv_out_channels
|
||||
self.fc_out_channels = fc_out_channels
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.with_dcn = with_dcn
|
||||
self.short_cut = short_cut
|
||||
|
||||
# add shared convs and fcs
|
||||
self.shared_convs, self.shared_fcs, last_layer_dim = \
|
||||
self._add_conv_fc_branch(
|
||||
self.num_shared_convs, self.num_shared_fcs, self.in_channels,
|
||||
True)
|
||||
self.shared_out_channels = last_layer_dim
|
||||
|
||||
# add cls specific branch
|
||||
self.cls_convs, self.cls_fcs, self.cls_last_dim = \
|
||||
self._add_conv_fc_branch(
|
||||
self.num_cls_convs, self.num_cls_fcs, self.shared_out_channels)
|
||||
|
||||
# add reg specific branch
|
||||
self.reg_convs, self.reg_fcs, self.reg_last_dim = \
|
||||
self._add_conv_fc_branch(
|
||||
self.num_reg_convs, self.num_reg_fcs, self.shared_out_channels)
|
||||
|
||||
if self.num_shared_fcs == 0 and not self.with_avg_pool:
|
||||
if self.num_cls_fcs == 0:
|
||||
self.cls_last_dim *= self.roi_feat_area
|
||||
if self.num_reg_fcs == 0:
|
||||
self.reg_last_dim *= self.roi_feat_area
|
||||
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
# reconstruct fc_cls and fc_reg since input channels are changed
|
||||
if self.with_cls:
|
||||
if self.custom_cls_channels:
|
||||
cls_channels = self.loss_cls.get_cls_channels(self.num_classes)
|
||||
else:
|
||||
cls_channels = self.num_classes + 1
|
||||
self.fc_cls = build_linear_layer(
|
||||
self.cls_predictor_cfg,
|
||||
in_features=self.cls_last_dim,
|
||||
out_features=cls_channels)
|
||||
if self.with_reg:
|
||||
out_dim_reg = (4 if self.reg_class_agnostic else 4 *
|
||||
self.num_classes)
|
||||
self.fc_reg = build_linear_layer(
|
||||
self.reg_predictor_cfg,
|
||||
in_features=self.reg_last_dim,
|
||||
out_features=out_dim_reg)
|
||||
|
||||
if init_cfg is None:
|
||||
# when init_cfg is None,
|
||||
# It has been set to
|
||||
# [[dict(type='Normal', std=0.01, override=dict(name='fc_cls'))],
|
||||
# [dict(type='Normal', std=0.001, override=dict(name='fc_reg'))]
|
||||
# after `super(ConvFCBBoxHead, self).__init__()`
|
||||
# we only need to append additional configuration
|
||||
# for `shared_fcs`, `cls_fcs` and `reg_fcs`
|
||||
self.init_cfg += [
|
||||
dict(
|
||||
type='Xavier',
|
||||
distribution='uniform',
|
||||
override=[
|
||||
dict(name='shared_fcs'),
|
||||
dict(name='cls_fcs'),
|
||||
dict(name='reg_fcs')
|
||||
])
|
||||
]
|
||||
|
||||
def _add_conv_fc_branch(self,
|
||||
num_branch_convs,
|
||||
num_branch_fcs,
|
||||
in_channels,
|
||||
is_shared=False):
|
||||
"""Add shared or separable branch.
|
||||
|
||||
convs -> avg pool (optional) -> fcs
|
||||
"""
|
||||
last_layer_dim = in_channels
|
||||
# add branch specific conv layers
|
||||
branch_convs = nn.ModuleList()
|
||||
if num_branch_convs > 0:
|
||||
for i in range(num_branch_convs):
|
||||
conv_in_channels = (
|
||||
last_layer_dim if i == 0 else self.conv_out_channels)
|
||||
if self.with_dcn:
|
||||
assert False, 'TODO: support DCNv4 in the task head'
|
||||
conv = DCNv4.DCNv4(
|
||||
conv_in_channels,
|
||||
self.conv_out_channels,
|
||||
)
|
||||
else:
|
||||
conv = ConvModule(
|
||||
conv_in_channels,
|
||||
self.conv_out_channels,
|
||||
3,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg)
|
||||
|
||||
branch_convs.append(conv)
|
||||
last_layer_dim = self.conv_out_channels
|
||||
# add branch specific fc layers
|
||||
branch_fcs = nn.ModuleList()
|
||||
if num_branch_fcs > 0:
|
||||
# for shared branch, only consider self.with_avg_pool
|
||||
# for separated branches, also consider self.num_shared_fcs
|
||||
if (is_shared
|
||||
or self.num_shared_fcs == 0) and not self.with_avg_pool:
|
||||
last_layer_dim *= self.roi_feat_area
|
||||
for i in range(num_branch_fcs):
|
||||
fc_in_channels = (
|
||||
last_layer_dim if i == 0 else self.fc_out_channels)
|
||||
branch_fcs.append(
|
||||
nn.Linear(fc_in_channels, self.fc_out_channels))
|
||||
last_layer_dim = self.fc_out_channels
|
||||
return branch_convs, branch_fcs, last_layer_dim
|
||||
|
||||
def forward(self, x):
|
||||
# shared part
|
||||
if self.with_dcn:
|
||||
N, C, H, W = x.shape
|
||||
x = x.permute(0, 2, 3, 1).view(N, H*W, C)
|
||||
if self.num_shared_convs > 0:
|
||||
for conv in self.shared_convs:
|
||||
if self.short_cut:
|
||||
x = x + conv(x, shape=(H, W))
|
||||
else:
|
||||
x = conv(x, shape=(H, W))
|
||||
else:
|
||||
if self.num_shared_convs > 0:
|
||||
for conv in self.shared_convs:
|
||||
x = conv(x)
|
||||
if self.num_shared_fcs > 0:
|
||||
if self.with_avg_pool:
|
||||
x = self.avg_pool(x)
|
||||
x = x.flatten(1)
|
||||
|
||||
for fc in self.shared_fcs:
|
||||
x = self.relu(fc(x))
|
||||
# separate branches
|
||||
x_cls = x
|
||||
x_reg = x
|
||||
|
||||
for conv in self.cls_convs:
|
||||
x_cls = conv(x_cls)
|
||||
if x_cls.dim() > 2:
|
||||
if self.with_avg_pool:
|
||||
x_cls = self.avg_pool(x_cls)
|
||||
x_cls = x_cls.flatten(1)
|
||||
for fc in self.cls_fcs:
|
||||
x_cls = self.relu(fc(x_cls))
|
||||
|
||||
for conv in self.reg_convs:
|
||||
x_reg = conv(x_reg)
|
||||
if x_reg.dim() > 2:
|
||||
if self.with_avg_pool:
|
||||
x_reg = self.avg_pool(x_reg)
|
||||
x_reg = x_reg.flatten(1)
|
||||
for fc in self.reg_fcs:
|
||||
x_reg = self.relu(fc(x_reg))
|
||||
|
||||
cls_score = self.fc_cls(x_cls) if self.with_cls else None
|
||||
bbox_pred = self.fc_reg(x_reg) if self.with_reg else None
|
||||
return cls_score, bbox_pred
|
||||
@@ -0,0 +1,332 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from mmcv.cnn import Linear, bias_init_with_prob, constant_init
|
||||
from mmcv.runner import force_fp32
|
||||
|
||||
from mmdet.core import multi_apply
|
||||
from mmdet.models.utils.transformer import inverse_sigmoid
|
||||
from mmdet.models.builder import HEADS
|
||||
from .detr_head import DETRHead
|
||||
|
||||
|
||||
@HEADS.register_module(force=True)
|
||||
class DeformableDETRHead(DETRHead):
|
||||
"""Head of DeformDETR: Deformable DETR: Deformable Transformers for End-to-
|
||||
End Object Detection.
|
||||
|
||||
Code is modified from the `official github repo
|
||||
<https://github.com/fundamentalvision/Deformable-DETR>`_.
|
||||
|
||||
More details can be found in the `paper
|
||||
<https://arxiv.org/abs/2010.04159>`_ .
|
||||
|
||||
Args:
|
||||
with_box_refine (bool): Whether to refine the reference points
|
||||
in the decoder. Defaults to False.
|
||||
as_two_stage (bool) : Whether to generate the proposal from
|
||||
the outputs of encoder.
|
||||
transformer (obj:`ConfigDict`): ConfigDict is used for building
|
||||
the Encoder and Decoder.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
*args,
|
||||
with_box_refine=False,
|
||||
as_two_stage=False,
|
||||
transformer=None,
|
||||
use_2fc_cls_branch=False,
|
||||
**kwargs):
|
||||
self.with_box_refine = with_box_refine
|
||||
self.as_two_stage = as_two_stage
|
||||
self.use_2fc_cls_branch = use_2fc_cls_branch
|
||||
if self.as_two_stage:
|
||||
transformer['as_two_stage'] = self.as_two_stage
|
||||
|
||||
super(DeformableDETRHead, self).__init__(
|
||||
*args, transformer=transformer, **kwargs)
|
||||
|
||||
def _init_layers(self):
|
||||
"""Initialize classification branch and regression branch of head."""
|
||||
|
||||
if not self.use_2fc_cls_branch:
|
||||
fc_cls = Linear(self.embed_dims, self.cls_out_channels)
|
||||
else:
|
||||
fc_cls = nn.Sequential(*[
|
||||
Linear(self.embed_dims, int(self.embed_dims * 1.5)),
|
||||
nn.LayerNorm(int(self.embed_dims * 1.5)),
|
||||
nn.GELU(),
|
||||
Linear(int(self.embed_dims * 1.5), self.cls_out_channels),
|
||||
])
|
||||
fc_cls.out_features = self.cls_out_channels
|
||||
|
||||
reg_branch = []
|
||||
for _ in range(self.num_reg_fcs):
|
||||
reg_branch.append(Linear(self.embed_dims, self.embed_dims))
|
||||
reg_branch.append(nn.ReLU())
|
||||
reg_branch.append(Linear(self.embed_dims, 4))
|
||||
reg_branch = nn.Sequential(*reg_branch)
|
||||
|
||||
def _get_clones(module, N):
|
||||
return nn.ModuleList([copy.deepcopy(module) for i in range(N)])
|
||||
|
||||
# last reg_branch is used to generate proposal from
|
||||
# encode feature map when as_two_stage is True.
|
||||
num_pred = (self.transformer.decoder.num_layers + 1) if \
|
||||
self.as_two_stage else self.transformer.decoder.num_layers
|
||||
|
||||
if self.with_box_refine:
|
||||
self.cls_branches = _get_clones(fc_cls, num_pred)
|
||||
self.reg_branches = _get_clones(reg_branch, num_pred)
|
||||
else:
|
||||
|
||||
self.cls_branches = nn.ModuleList(
|
||||
[fc_cls for _ in range(num_pred)])
|
||||
self.reg_branches = nn.ModuleList(
|
||||
[reg_branch for _ in range(num_pred)])
|
||||
|
||||
if not self.as_two_stage:
|
||||
self.query_embedding = nn.Embedding(
|
||||
self.num_query,
|
||||
self.embed_dims * 2)
|
||||
|
||||
def init_weights(self):
|
||||
"""Initialize weights of the DeformDETR head."""
|
||||
self.transformer.init_weights()
|
||||
if self.loss_cls.use_sigmoid:
|
||||
bias_init = bias_init_with_prob(0.01)
|
||||
if not self.use_2fc_cls_branch:
|
||||
for m in self.cls_branches:
|
||||
nn.init.constant_(m.bias, bias_init)
|
||||
for m in self.reg_branches:
|
||||
constant_init(m[-1], 0, bias=0)
|
||||
nn.init.constant_(self.reg_branches[0][-1].bias.data[2:], -2.0)
|
||||
if self.as_two_stage:
|
||||
for m in self.reg_branches:
|
||||
nn.init.constant_(m[-1].bias.data[2:], 0.0)
|
||||
|
||||
def forward(self, mlvl_feats, img_metas):
|
||||
"""Forward function.
|
||||
|
||||
Args:
|
||||
mlvl_feats (tuple[Tensor]): Features from the upstream
|
||||
network, each is a 4D-tensor with shape
|
||||
(N, C, H, W).
|
||||
img_metas (list[dict]): List of image information.
|
||||
|
||||
Returns:
|
||||
all_cls_scores (Tensor): Outputs from the classification head, \
|
||||
shape [nb_dec, bs, num_query, cls_out_channels]. Note \
|
||||
cls_out_channels should includes background.
|
||||
all_bbox_preds (Tensor): Sigmoid outputs from the regression \
|
||||
head with normalized coordinate format (cx, cy, w, h). \
|
||||
Shape [nb_dec, bs, num_query, 4].
|
||||
enc_outputs_class (Tensor): The score of each point on encode \
|
||||
feature map, has shape (N, h*w, num_class). Only when \
|
||||
as_two_stage is True it would be returned, otherwise \
|
||||
`None` would be returned.
|
||||
enc_outputs_coord (Tensor): The proposal generate from the \
|
||||
encode feature map, has shape (N, h*w, 4). Only when \
|
||||
as_two_stage is True it would be returned, otherwise \
|
||||
`None` would be returned.
|
||||
"""
|
||||
|
||||
batch_size = mlvl_feats[0].size(0)
|
||||
input_img_h, input_img_w = img_metas[0]['batch_input_shape']
|
||||
img_masks = mlvl_feats[0].new_ones(
|
||||
(batch_size, input_img_h, input_img_w))
|
||||
for img_id in range(batch_size):
|
||||
img_h, img_w, _ = img_metas[img_id]['img_shape']
|
||||
img_masks[img_id, :img_h, :img_w] = 0
|
||||
|
||||
mlvl_masks = []
|
||||
mlvl_positional_encodings = []
|
||||
for feat in mlvl_feats:
|
||||
mlvl_masks.append(
|
||||
F.interpolate(img_masks[None],
|
||||
size=feat.shape[-2:]).to(torch.bool).squeeze(0))
|
||||
mlvl_positional_encodings.append(
|
||||
self.positional_encoding(mlvl_masks[-1]))
|
||||
|
||||
query_embeds = None
|
||||
if not self.as_two_stage:
|
||||
query_embeds = self.query_embedding.weight
|
||||
hs, init_reference, inter_references, \
|
||||
enc_outputs_class, enc_outputs_coord = self.transformer(
|
||||
mlvl_feats,
|
||||
mlvl_masks,
|
||||
query_embeds,
|
||||
mlvl_positional_encodings,
|
||||
reg_branches=self.reg_branches if self.with_box_refine else None, # noqa:E501
|
||||
cls_branches=self.cls_branches if self.as_two_stage else None # noqa:E501
|
||||
)
|
||||
hs = hs.permute(0, 2, 1, 3)
|
||||
outputs_classes = []
|
||||
outputs_coords = []
|
||||
|
||||
for lvl in range(hs.shape[0]):
|
||||
if lvl == 0:
|
||||
reference = init_reference
|
||||
else:
|
||||
reference = inter_references[lvl - 1]
|
||||
reference = inverse_sigmoid(reference)
|
||||
outputs_class = self.cls_branches[lvl](hs[lvl])
|
||||
tmp = self.reg_branches[lvl](hs[lvl])
|
||||
if reference.shape[-1] == 4:
|
||||
tmp += reference
|
||||
else:
|
||||
assert reference.shape[-1] == 2
|
||||
tmp[..., :2] += reference
|
||||
outputs_coord = tmp.sigmoid()
|
||||
outputs_classes.append(outputs_class)
|
||||
outputs_coords.append(outputs_coord)
|
||||
|
||||
outputs_classes = torch.stack(outputs_classes)
|
||||
outputs_coords = torch.stack(outputs_coords)
|
||||
if self.as_two_stage:
|
||||
return outputs_classes, outputs_coords, \
|
||||
enc_outputs_class, \
|
||||
enc_outputs_coord.sigmoid()
|
||||
else:
|
||||
return outputs_classes, outputs_coords, \
|
||||
None, None
|
||||
|
||||
@force_fp32(apply_to=('all_cls_scores_list', 'all_bbox_preds_list'))
|
||||
def loss(self,
|
||||
all_cls_scores,
|
||||
all_bbox_preds,
|
||||
enc_cls_scores,
|
||||
enc_bbox_preds,
|
||||
gt_bboxes_list,
|
||||
gt_labels_list,
|
||||
img_metas,
|
||||
gt_bboxes_ignore=None):
|
||||
""""Loss function.
|
||||
|
||||
Args:
|
||||
all_cls_scores (Tensor): Classification score of all
|
||||
decoder layers, has shape
|
||||
[nb_dec, bs, num_query, cls_out_channels].
|
||||
all_bbox_preds (Tensor): Sigmoid regression
|
||||
outputs of all decode layers. Each is a 4D-tensor with
|
||||
normalized coordinate format (cx, cy, w, h) and shape
|
||||
[nb_dec, bs, num_query, 4].
|
||||
enc_cls_scores (Tensor): Classification scores of
|
||||
points on encode feature map , has shape
|
||||
(N, h*w, num_classes). Only be passed when as_two_stage is
|
||||
True, otherwise is None.
|
||||
enc_bbox_preds (Tensor): Regression results of each points
|
||||
on the encode feature map, has shape (N, h*w, 4). Only be
|
||||
passed when as_two_stage is True, otherwise is None.
|
||||
gt_bboxes_list (list[Tensor]): Ground truth bboxes for each image
|
||||
with shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.
|
||||
gt_labels_list (list[Tensor]): Ground truth class indices for each
|
||||
image with shape (num_gts, ).
|
||||
img_metas (list[dict]): List of image meta information.
|
||||
gt_bboxes_ignore (list[Tensor], optional): Bounding boxes
|
||||
which can be ignored for each image. Default None.
|
||||
|
||||
Returns:
|
||||
dict[str, Tensor]: A dictionary of loss components.
|
||||
"""
|
||||
assert gt_bboxes_ignore is None, \
|
||||
f'{self.__class__.__name__} only supports ' \
|
||||
f'for gt_bboxes_ignore setting to None.'
|
||||
|
||||
num_dec_layers = len(all_cls_scores)
|
||||
all_gt_bboxes_list = [gt_bboxes_list for _ in range(num_dec_layers)]
|
||||
all_gt_labels_list = [gt_labels_list for _ in range(num_dec_layers)]
|
||||
all_gt_bboxes_ignore_list = [
|
||||
gt_bboxes_ignore for _ in range(num_dec_layers)
|
||||
]
|
||||
img_metas_list = [img_metas for _ in range(num_dec_layers)]
|
||||
|
||||
losses_cls, losses_bbox, losses_iou = multi_apply(
|
||||
self.loss_single, all_cls_scores, all_bbox_preds,
|
||||
all_gt_bboxes_list, all_gt_labels_list, img_metas_list,
|
||||
all_gt_bboxes_ignore_list)
|
||||
|
||||
loss_dict = dict()
|
||||
# loss of proposal generated from encode feature map.
|
||||
if enc_cls_scores is not None:
|
||||
binary_labels_list = [
|
||||
torch.zeros_like(gt_labels_list[i])
|
||||
for i in range(len(img_metas))
|
||||
]
|
||||
enc_loss_cls, enc_losses_bbox, enc_losses_iou = \
|
||||
self.loss_single(enc_cls_scores, enc_bbox_preds,
|
||||
gt_bboxes_list, binary_labels_list,
|
||||
img_metas, gt_bboxes_ignore)
|
||||
loss_dict['enc_loss_cls'] = enc_loss_cls
|
||||
loss_dict['enc_loss_bbox'] = enc_losses_bbox
|
||||
loss_dict['enc_loss_iou'] = enc_losses_iou
|
||||
|
||||
# loss from the last decoder layer
|
||||
loss_dict['loss_cls'] = losses_cls[-1]
|
||||
loss_dict['loss_bbox'] = losses_bbox[-1]
|
||||
loss_dict['loss_iou'] = losses_iou[-1]
|
||||
# loss from other decoder layers
|
||||
num_dec_layer = 0
|
||||
for loss_cls_i, loss_bbox_i, loss_iou_i in zip(losses_cls[:-1],
|
||||
losses_bbox[:-1],
|
||||
losses_iou[:-1]):
|
||||
loss_dict[f'd{num_dec_layer}.loss_cls'] = loss_cls_i
|
||||
loss_dict[f'd{num_dec_layer}.loss_bbox'] = loss_bbox_i
|
||||
loss_dict[f'd{num_dec_layer}.loss_iou'] = loss_iou_i
|
||||
num_dec_layer += 1
|
||||
return loss_dict
|
||||
|
||||
@force_fp32(apply_to=('all_cls_scores_list', 'all_bbox_preds_list'))
|
||||
def get_bboxes(self,
|
||||
all_cls_scores,
|
||||
all_bbox_preds,
|
||||
enc_cls_scores,
|
||||
enc_bbox_preds,
|
||||
img_metas,
|
||||
rescale=False):
|
||||
"""Transform network outputs for a batch into bbox predictions.
|
||||
|
||||
Args:
|
||||
all_cls_scores (Tensor): Classification score of all
|
||||
decoder layers, has shape
|
||||
[nb_dec, bs, num_query, cls_out_channels].
|
||||
all_bbox_preds (Tensor): Sigmoid regression
|
||||
outputs of all decode layers. Each is a 4D-tensor with
|
||||
normalized coordinate format (cx, cy, w, h) and shape
|
||||
[nb_dec, bs, num_query, 4].
|
||||
enc_cls_scores (Tensor): Classification scores of
|
||||
points on encode feature map , has shape
|
||||
(N, h*w, num_classes). Only be passed when as_two_stage is
|
||||
True, otherwise is None.
|
||||
enc_bbox_preds (Tensor): Regression results of each points
|
||||
on the encode feature map, has shape (N, h*w, 4). Only be
|
||||
passed when as_two_stage is True, otherwise is None.
|
||||
img_metas (list[dict]): Meta information of each image.
|
||||
rescale (bool, optional): If True, return boxes in original
|
||||
image space. Default False.
|
||||
|
||||
Returns:
|
||||
list[list[Tensor, Tensor]]: Each item in result_list is 2-tuple. \
|
||||
The first item is an (n, 5) tensor, where the first 4 columns \
|
||||
are bounding box positions (tl_x, tl_y, br_x, br_y) and the \
|
||||
5-th column is a score between 0 and 1. The second item is a \
|
||||
(n,) tensor where each item is the predicted class label of \
|
||||
the corresponding box.
|
||||
"""
|
||||
cls_scores = all_cls_scores[-1]
|
||||
bbox_preds = all_bbox_preds[-1]
|
||||
|
||||
result_list = []
|
||||
for img_id in range(len(img_metas)):
|
||||
cls_score = cls_scores[img_id]
|
||||
bbox_pred = bbox_preds[img_id]
|
||||
img_shape = img_metas[img_id]['img_shape']
|
||||
scale_factor = img_metas[img_id]['scale_factor']
|
||||
proposals = self._get_bboxes_single(cls_score, bbox_pred,
|
||||
img_shape, scale_factor,
|
||||
rescale)
|
||||
result_list.append(proposals)
|
||||
return result_list
|
||||
954
detection/mmdet_custom/models/dense_heads/detr_head.py
Normal file
954
detection/mmdet_custom/models/dense_heads/detr_head.py
Normal file
@@ -0,0 +1,954 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from mmcv.cnn import Conv2d, Linear, build_activation_layer
|
||||
from mmcv.cnn.bricks.transformer import FFN, build_positional_encoding
|
||||
from mmcv.runner import force_fp32
|
||||
|
||||
from mmdet.core import (bbox_cxcywh_to_xyxy, bbox_xyxy_to_cxcywh,
|
||||
build_assigner, build_sampler, multi_apply,
|
||||
reduce_mean)
|
||||
from mmdet.models.utils import build_transformer
|
||||
from mmdet.models.builder import HEADS, build_loss
|
||||
from mmdet.models.dense_heads.anchor_free_head import AnchorFreeHead
|
||||
import numpy as np
|
||||
|
||||
|
||||
@HEADS.register_module(force=True)
|
||||
class DETRHead(AnchorFreeHead):
|
||||
"""Implements the DETR transformer head.
|
||||
|
||||
See `paper: End-to-End Object Detection with Transformers
|
||||
<https://arxiv.org/pdf/2005.12872>`_ for details.
|
||||
|
||||
Args:
|
||||
num_classes (int): Number of categories excluding the background.
|
||||
in_channels (int): Number of channels in the input feature map.
|
||||
num_query (int): Number of query in Transformer.
|
||||
num_reg_fcs (int, optional): Number of fully-connected layers used in
|
||||
`FFN`, which is then used for the regression head. Default 2.
|
||||
transformer (obj:`mmcv.ConfigDict`|dict): Config for transformer.
|
||||
Default: None.
|
||||
sync_cls_avg_factor (bool): Whether to sync the avg_factor of
|
||||
all ranks. Default to False.
|
||||
positional_encoding (obj:`mmcv.ConfigDict`|dict):
|
||||
Config for position encoding.
|
||||
loss_cls (obj:`mmcv.ConfigDict`|dict): Config of the
|
||||
classification loss. Default `CrossEntropyLoss`.
|
||||
loss_bbox (obj:`mmcv.ConfigDict`|dict): Config of the
|
||||
regression loss. Default `L1Loss`.
|
||||
loss_iou (obj:`mmcv.ConfigDict`|dict): Config of the
|
||||
regression iou loss. Default `GIoULoss`.
|
||||
tran_cfg (obj:`mmcv.ConfigDict`|dict): Training config of
|
||||
transformer head.
|
||||
test_cfg (obj:`mmcv.ConfigDict`|dict): Testing config of
|
||||
transformer head.
|
||||
init_cfg (dict or list[dict], optional): Initialization config dict.
|
||||
Default: None
|
||||
"""
|
||||
|
||||
_version = 2
|
||||
|
||||
def __init__(self,
|
||||
num_classes,
|
||||
in_channels,
|
||||
num_query=100,
|
||||
num_reg_fcs=2,
|
||||
transformer=None,
|
||||
sync_cls_avg_factor=False,
|
||||
positional_encoding=dict(
|
||||
type='SinePositionalEncoding',
|
||||
num_feats=128,
|
||||
normalize=True),
|
||||
loss_cls=dict(
|
||||
type='CrossEntropyLoss',
|
||||
bg_cls_weight=0.1,
|
||||
use_sigmoid=False,
|
||||
loss_weight=1.0,
|
||||
class_weight=1.0),
|
||||
loss_bbox=dict(type='L1Loss', loss_weight=5.0),
|
||||
loss_iou=dict(type='GIoULoss', loss_weight=2.0),
|
||||
train_cfg=dict(
|
||||
assigner=dict(
|
||||
type='HungarianAssigner',
|
||||
cls_cost=dict(type='ClassificationCost', weight=1.),
|
||||
reg_cost=dict(type='BBoxL1Cost', weight=5.0),
|
||||
iou_cost=dict(
|
||||
type='IoUCost', iou_mode='giou', weight=2.0))),
|
||||
test_cfg=dict(max_per_img=100),
|
||||
init_cfg=None,
|
||||
**kwargs):
|
||||
# NOTE here use `AnchorFreeHead` instead of `TransformerHead`,
|
||||
# since it brings inconvenience when the initialization of
|
||||
# `AnchorFreeHead` is called.
|
||||
super(AnchorFreeHead, self).__init__(init_cfg)
|
||||
|
||||
self.bg_cls_weight = 0
|
||||
self.sync_cls_avg_factor = sync_cls_avg_factor
|
||||
class_weight = loss_cls.get('class_weight', None)
|
||||
if class_weight is not None and (self.__class__ is DETRHead):
|
||||
# assert isinstance(class_weight, float), 'Expected ' \
|
||||
# 'class_weight to have type float. Found ' \
|
||||
# f'{type(class_weight)}.'
|
||||
|
||||
# NOTE following the official DETR rep0, bg_cls_weight means
|
||||
# relative classification weight of the no-object class.
|
||||
bg_cls_weight = loss_cls.get('bg_cls_weight', class_weight)
|
||||
|
||||
assert isinstance(bg_cls_weight, float), 'Expected ' \
|
||||
'bg_cls_weight to have type float. Found ' \
|
||||
f'{type(bg_cls_weight)}.'
|
||||
if isinstance(class_weight, list):
|
||||
class_weight.append(bg_cls_weight)
|
||||
class_weight = np.array(class_weight)
|
||||
class_weight = torch.from_numpy(class_weight)
|
||||
class_weight = torch.ones(num_classes + 1) * class_weight
|
||||
elif isinstance(class_weight, float):
|
||||
class_weight = torch.ones(num_classes + 1) * class_weight
|
||||
# set background class as the last indice
|
||||
class_weight[num_classes] = bg_cls_weight
|
||||
loss_cls.update({'class_weight': class_weight})
|
||||
if 'bg_cls_weight' in loss_cls:
|
||||
loss_cls.pop('bg_cls_weight')
|
||||
self.bg_cls_weight = bg_cls_weight
|
||||
|
||||
if train_cfg:
|
||||
assert 'assigner' in train_cfg, 'assigner should be provided ' \
|
||||
'when train_cfg is set.'
|
||||
assigner = train_cfg['assigner']
|
||||
# assert loss_cls['loss_weight'] == assigner['cls_cost']['weight'],
|
||||
# 'The classification weight for loss and matcher should be' \
|
||||
# 'exactly the same.'
|
||||
# assert loss_bbox['loss_weight'] == assigner['reg_cost'][
|
||||
# 'weight'], 'The regression L1 weight for loss and matcher '\
|
||||
# 'should be exactly the same.'
|
||||
# assert loss_iou['loss_weight'] == assigner['iou_cost']['weight'],
|
||||
# 'The regression iou weight for loss and matcher should be' \
|
||||
# 'exactly the same.'
|
||||
self.assigner = build_assigner(assigner)
|
||||
# DETR sampling=False, so use PseudoSampler
|
||||
sampler_cfg = dict(type='PseudoSampler')
|
||||
self.sampler = build_sampler(sampler_cfg, context=self)
|
||||
|
||||
self.num_query = num_query
|
||||
self.num_classes = num_classes
|
||||
self.in_channels = in_channels
|
||||
self.num_reg_fcs = num_reg_fcs
|
||||
self.train_cfg = train_cfg
|
||||
self.test_cfg = test_cfg
|
||||
self.fp16_enabled = False
|
||||
self.loss_cls = build_loss(loss_cls)
|
||||
self.loss_bbox = build_loss(loss_bbox)
|
||||
self.loss_iou = build_loss(loss_iou)
|
||||
|
||||
if self.loss_cls.use_sigmoid:
|
||||
self.cls_out_channels = num_classes
|
||||
else:
|
||||
self.cls_out_channels = num_classes + 1
|
||||
self.act_cfg = transformer.get('act_cfg',
|
||||
dict(type='ReLU', inplace=True))
|
||||
self.activate = build_activation_layer(self.act_cfg)
|
||||
self.positional_encoding = build_positional_encoding(
|
||||
positional_encoding)
|
||||
self.transformer = build_transformer(transformer)
|
||||
self.embed_dims = self.transformer.embed_dims
|
||||
assert 'num_feats' in positional_encoding
|
||||
num_feats = positional_encoding['num_feats']
|
||||
assert num_feats * 2 == self.embed_dims, 'embed_dims should' \
|
||||
f' be exactly 2 times of num_feats. Found {self.embed_dims}' \
|
||||
f' and {num_feats}.'
|
||||
|
||||
self._init_layers()
|
||||
|
||||
def _init_layers(self):
|
||||
"""Initialize layers of the transformer head."""
|
||||
self.input_proj = Conv2d(
|
||||
self.in_channels, self.embed_dims, kernel_size=1)
|
||||
self.fc_cls = Linear(self.embed_dims, self.cls_out_channels)
|
||||
self.reg_ffn = FFN(
|
||||
self.embed_dims,
|
||||
self.embed_dims,
|
||||
self.num_reg_fcs,
|
||||
self.act_cfg,
|
||||
dropout=0.0,
|
||||
add_residual=False)
|
||||
self.fc_reg = Linear(self.embed_dims, 4)
|
||||
self.query_embedding = nn.Embedding(self.num_query, self.embed_dims)
|
||||
|
||||
def init_weights(self):
|
||||
"""Initialize weights of the transformer head."""
|
||||
# The initialization for transformer is important
|
||||
self.transformer.init_weights()
|
||||
|
||||
def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict,
|
||||
missing_keys, unexpected_keys, error_msgs):
|
||||
"""load checkpoints."""
|
||||
# NOTE here use `AnchorFreeHead` instead of `TransformerHead`,
|
||||
# since `AnchorFreeHead._load_from_state_dict` should not be
|
||||
# called here. Invoking the default `Module._load_from_state_dict`
|
||||
# is enough.
|
||||
|
||||
# Names of some parameters in has been changed.
|
||||
version = local_metadata.get('version', None)
|
||||
if (version is None or version < 2) and self.__class__ is DETRHead:
|
||||
convert_dict = {
|
||||
'.self_attn.': '.attentions.0.',
|
||||
'.ffn.': '.ffns.0.',
|
||||
'.multihead_attn.': '.attentions.1.',
|
||||
'.decoder.norm.': '.decoder.post_norm.'
|
||||
}
|
||||
state_dict_keys = list(state_dict.keys())
|
||||
for k in state_dict_keys:
|
||||
for ori_key, convert_key in convert_dict.items():
|
||||
if ori_key in k:
|
||||
convert_key = k.replace(ori_key, convert_key)
|
||||
state_dict[convert_key] = state_dict[k]
|
||||
del state_dict[k]
|
||||
|
||||
super(AnchorFreeHead,
|
||||
self)._load_from_state_dict(state_dict, prefix, local_metadata,
|
||||
strict, missing_keys,
|
||||
unexpected_keys, error_msgs)
|
||||
|
||||
def forward(self, feats, img_metas):
|
||||
"""Forward function.
|
||||
|
||||
Args:
|
||||
feats (tuple[Tensor]): Features from the upstream network, each is
|
||||
a 4D-tensor.
|
||||
img_metas (list[dict]): List of image information.
|
||||
|
||||
Returns:
|
||||
tuple[list[Tensor], list[Tensor]]: Outputs for all scale levels.
|
||||
|
||||
- all_cls_scores_list (list[Tensor]): Classification scores \
|
||||
for each scale level. Each is a 4D-tensor with shape \
|
||||
[nb_dec, bs, num_query, cls_out_channels]. Note \
|
||||
`cls_out_channels` should includes background.
|
||||
- all_bbox_preds_list (list[Tensor]): Sigmoid regression \
|
||||
outputs for each scale level. Each is a 4D-tensor with \
|
||||
normalized coordinate format (cx, cy, w, h) and shape \
|
||||
[nb_dec, bs, num_query, 4].
|
||||
"""
|
||||
num_levels = len(feats)
|
||||
img_metas_list = [img_metas for _ in range(num_levels)]
|
||||
return multi_apply(self.forward_single, feats, img_metas_list)
|
||||
|
||||
def forward_single(self, x, img_metas):
|
||||
""""Forward function for a single feature level.
|
||||
|
||||
Args:
|
||||
x (Tensor): Input feature from backbone's single stage, shape
|
||||
[bs, c, h, w].
|
||||
img_metas (list[dict]): List of image information.
|
||||
|
||||
Returns:
|
||||
all_cls_scores (Tensor): Outputs from the classification head,
|
||||
shape [nb_dec, bs, num_query, cls_out_channels]. Note
|
||||
cls_out_channels should includes background.
|
||||
all_bbox_preds (Tensor): Sigmoid outputs from the regression
|
||||
head with normalized coordinate format (cx, cy, w, h).
|
||||
Shape [nb_dec, bs, num_query, 4].
|
||||
"""
|
||||
# construct binary masks which used for the transformer.
|
||||
# NOTE following the official DETR repo, non-zero values representing
|
||||
# ignored positions, while zero values means valid positions.
|
||||
batch_size = x.size(0)
|
||||
input_img_h, input_img_w = img_metas[0]['batch_input_shape']
|
||||
masks = x.new_ones((batch_size, input_img_h, input_img_w))
|
||||
for img_id in range(batch_size):
|
||||
img_h, img_w, _ = img_metas[img_id]['img_shape']
|
||||
masks[img_id, :img_h, :img_w] = 0
|
||||
|
||||
x = self.input_proj(x)
|
||||
# interpolate masks to have the same spatial shape with x
|
||||
masks = F.interpolate(
|
||||
masks.unsqueeze(1), size=x.shape[-2:]).to(torch.bool).squeeze(1)
|
||||
# position encoding
|
||||
pos_embed = self.positional_encoding(masks) # [bs, embed_dim, h, w]
|
||||
# outs_dec: [nb_dec, bs, num_query, embed_dim]
|
||||
outs_dec, _ = self.transformer(x, masks, self.query_embedding.weight,
|
||||
pos_embed)
|
||||
|
||||
all_cls_scores = self.fc_cls(outs_dec)
|
||||
all_bbox_preds = self.fc_reg(self.activate(
|
||||
self.reg_ffn(outs_dec))).sigmoid()
|
||||
return all_cls_scores, all_bbox_preds
|
||||
|
||||
@force_fp32(apply_to=('all_cls_scores_list', 'all_bbox_preds_list'))
|
||||
def loss(self,
|
||||
all_cls_scores_list,
|
||||
all_bbox_preds_list,
|
||||
gt_bboxes_list,
|
||||
gt_labels_list,
|
||||
img_metas,
|
||||
gt_bboxes_ignore=None):
|
||||
""""Loss function.
|
||||
|
||||
Only outputs from the last feature level are used for computing
|
||||
losses by default.
|
||||
|
||||
Args:
|
||||
all_cls_scores_list (list[Tensor]): Classification outputs
|
||||
for each feature level. Each is a 4D-tensor with shape
|
||||
[nb_dec, bs, num_query, cls_out_channels].
|
||||
all_bbox_preds_list (list[Tensor]): Sigmoid regression
|
||||
outputs for each feature level. Each is a 4D-tensor with
|
||||
normalized coordinate format (cx, cy, w, h) and shape
|
||||
[nb_dec, bs, num_query, 4].
|
||||
gt_bboxes_list (list[Tensor]): Ground truth bboxes for each image
|
||||
with shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.
|
||||
gt_labels_list (list[Tensor]): Ground truth class indices for each
|
||||
image with shape (num_gts, ).
|
||||
img_metas (list[dict]): List of image meta information.
|
||||
gt_bboxes_ignore (list[Tensor], optional): Bounding boxes
|
||||
which can be ignored for each image. Default None.
|
||||
|
||||
Returns:
|
||||
dict[str, Tensor]: A dictionary of loss components.
|
||||
"""
|
||||
# NOTE defaultly only the outputs from the last feature scale is used.
|
||||
all_cls_scores = all_cls_scores_list[-1]
|
||||
all_bbox_preds = all_bbox_preds_list[-1]
|
||||
assert gt_bboxes_ignore is None, \
|
||||
'Only supports for gt_bboxes_ignore setting to None.'
|
||||
|
||||
num_dec_layers = len(all_cls_scores)
|
||||
all_gt_bboxes_list = [gt_bboxes_list for _ in range(num_dec_layers)]
|
||||
all_gt_labels_list = [gt_labels_list for _ in range(num_dec_layers)]
|
||||
all_gt_bboxes_ignore_list = [
|
||||
gt_bboxes_ignore for _ in range(num_dec_layers)
|
||||
]
|
||||
img_metas_list = [img_metas for _ in range(num_dec_layers)]
|
||||
|
||||
losses_cls, losses_bbox, losses_iou = multi_apply(
|
||||
self.loss_single, all_cls_scores, all_bbox_preds,
|
||||
all_gt_bboxes_list, all_gt_labels_list, img_metas_list,
|
||||
all_gt_bboxes_ignore_list)
|
||||
|
||||
loss_dict = dict()
|
||||
# loss from the last decoder layer
|
||||
loss_dict['loss_cls'] = losses_cls[-1]
|
||||
loss_dict['loss_bbox'] = losses_bbox[-1]
|
||||
loss_dict['loss_iou'] = losses_iou[-1]
|
||||
# loss from other decoder layers
|
||||
num_dec_layer = 0
|
||||
for loss_cls_i, loss_bbox_i, loss_iou_i in zip(losses_cls[:-1],
|
||||
losses_bbox[:-1],
|
||||
losses_iou[:-1]):
|
||||
loss_dict[f'd{num_dec_layer}.loss_cls'] = loss_cls_i
|
||||
loss_dict[f'd{num_dec_layer}.loss_bbox'] = loss_bbox_i
|
||||
loss_dict[f'd{num_dec_layer}.loss_iou'] = loss_iou_i
|
||||
num_dec_layer += 1
|
||||
return loss_dict
|
||||
|
||||
def get_fed_loss_classes(self, gt_classes, num_fed_loss_classes, num_classes, weight):
|
||||
"""
|
||||
Args:
|
||||
gt_classes: a long tensor of shape R that contains the gt class label of each proposal.
|
||||
num_fed_loss_classes: minimum number of classes to keep when calculating federated loss.
|
||||
Will sample negative classes if number of unique gt_classes is smaller than this value.
|
||||
num_classes: number of foreground classes
|
||||
weight: probabilities used to sample negative classes
|
||||
Returns:
|
||||
Tensor:
|
||||
classes to keep when calculating the federated loss, including both unique gt
|
||||
classes and sampled negative classes.
|
||||
"""
|
||||
unique_gt_classes = torch.unique(gt_classes)
|
||||
prob = unique_gt_classes.new_ones(num_classes + 1).float()
|
||||
prob[-1] = 0
|
||||
if len(unique_gt_classes) < num_fed_loss_classes:
|
||||
prob[:num_classes] = weight.float().clone()
|
||||
prob[unique_gt_classes] = 0
|
||||
sampled_negative_classes = torch.multinomial(
|
||||
prob, num_fed_loss_classes - len(unique_gt_classes), replacement=False
|
||||
)
|
||||
fed_loss_classes = torch.cat([unique_gt_classes, sampled_negative_classes])
|
||||
else:
|
||||
fed_loss_classes = unique_gt_classes
|
||||
return fed_loss_classes
|
||||
|
||||
def loss_single(self,
|
||||
cls_scores,
|
||||
bbox_preds,
|
||||
gt_bboxes_list,
|
||||
gt_labels_list,
|
||||
img_metas,
|
||||
gt_bboxes_ignore_list=None):
|
||||
""""Loss function for outputs from a single decoder layer of a single
|
||||
feature level.
|
||||
|
||||
Args:
|
||||
cls_scores (Tensor): Box score logits from a single decoder layer
|
||||
for all images. Shape [bs, num_query, cls_out_channels].
|
||||
bbox_preds (Tensor): Sigmoid outputs from a single decoder layer
|
||||
for all images, with normalized coordinate (cx, cy, w, h) and
|
||||
shape [bs, num_query, 4].
|
||||
gt_bboxes_list (list[Tensor]): Ground truth bboxes for each image
|
||||
with shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.
|
||||
gt_labels_list (list[Tensor]): Ground truth class indices for each
|
||||
image with shape (num_gts, ).
|
||||
img_metas (list[dict]): List of image meta information.
|
||||
gt_bboxes_ignore_list (list[Tensor], optional): Bounding
|
||||
boxes which can be ignored for each image. Default None.
|
||||
|
||||
Returns:
|
||||
dict[str, Tensor]: A dictionary of loss components for outputs from
|
||||
a single decoder layer.
|
||||
"""
|
||||
num_imgs = cls_scores.size(0)
|
||||
cls_scores_list = [cls_scores[i] for i in range(num_imgs)]
|
||||
bbox_preds_list = [bbox_preds[i] for i in range(num_imgs)]
|
||||
cls_reg_targets = self.get_targets(cls_scores_list, bbox_preds_list,
|
||||
gt_bboxes_list, gt_labels_list,
|
||||
img_metas, gt_bboxes_ignore_list)
|
||||
(labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,
|
||||
num_total_pos, num_total_neg) = cls_reg_targets
|
||||
|
||||
labels = torch.cat(labels_list, 0)
|
||||
label_weights = torch.cat(label_weights_list, 0)
|
||||
bbox_targets = torch.cat(bbox_targets_list, 0)
|
||||
bbox_weights = torch.cat(bbox_weights_list, 0)
|
||||
|
||||
# classification loss
|
||||
cls_scores = cls_scores.reshape(-1, self.cls_out_channels)
|
||||
# construct weighted avg_factor to match with the official DETR repo
|
||||
cls_avg_factor = num_total_pos * 1.0 + \
|
||||
num_total_neg * self.bg_cls_weight
|
||||
if self.sync_cls_avg_factor:
|
||||
cls_avg_factor = reduce_mean(
|
||||
cls_scores.new_tensor([cls_avg_factor]))
|
||||
cls_avg_factor = max(cls_avg_factor, 1)
|
||||
|
||||
loss_cls = self.loss_cls(
|
||||
cls_scores, labels, label_weights, avg_factor=cls_avg_factor)
|
||||
|
||||
# Compute the average number of gt boxes across all gpus, for
|
||||
# normalization purposes
|
||||
num_total_pos = loss_cls.new_tensor([num_total_pos])
|
||||
num_total_pos = torch.clamp(reduce_mean(num_total_pos), min=1).item()
|
||||
|
||||
# construct factors used for rescale bboxes
|
||||
factors = []
|
||||
for img_meta, bbox_pred in zip(img_metas, bbox_preds):
|
||||
img_h, img_w, _ = img_meta['img_shape']
|
||||
factor = bbox_pred.new_tensor([img_w, img_h, img_w,
|
||||
img_h]).unsqueeze(0).repeat(
|
||||
bbox_pred.size(0), 1)
|
||||
factors.append(factor)
|
||||
factors = torch.cat(factors, 0)
|
||||
|
||||
# DETR regress the relative position of boxes (cxcywh) in the image,
|
||||
# thus the learning target is normalized by the image size. So here
|
||||
# we need to re-scale them for calculating IoU loss
|
||||
bbox_preds = bbox_preds.reshape(-1, 4)
|
||||
bboxes = bbox_cxcywh_to_xyxy(bbox_preds) * factors
|
||||
bboxes_gt = bbox_cxcywh_to_xyxy(bbox_targets) * factors
|
||||
|
||||
# regression IoU loss, defaultly GIoU loss
|
||||
loss_iou = self.loss_iou(
|
||||
bboxes, bboxes_gt, bbox_weights, avg_factor=num_total_pos)
|
||||
|
||||
# regression L1 loss
|
||||
loss_bbox = self.loss_bbox(
|
||||
bbox_preds, bbox_targets, bbox_weights, avg_factor=num_total_pos)
|
||||
return loss_cls, loss_bbox, loss_iou
|
||||
|
||||
def get_targets(self,
|
||||
cls_scores_list,
|
||||
bbox_preds_list,
|
||||
gt_bboxes_list,
|
||||
gt_labels_list,
|
||||
img_metas,
|
||||
gt_bboxes_ignore_list=None):
|
||||
""""Compute regression and classification targets for a batch image.
|
||||
|
||||
Outputs from a single decoder layer of a single feature level are used.
|
||||
|
||||
Args:
|
||||
cls_scores_list (list[Tensor]): Box score logits from a single
|
||||
decoder layer for each image with shape [num_query,
|
||||
cls_out_channels].
|
||||
bbox_preds_list (list[Tensor]): Sigmoid outputs from a single
|
||||
decoder layer for each image, with normalized coordinate
|
||||
(cx, cy, w, h) and shape [num_query, 4].
|
||||
gt_bboxes_list (list[Tensor]): Ground truth bboxes for each image
|
||||
with shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.
|
||||
gt_labels_list (list[Tensor]): Ground truth class indices for each
|
||||
image with shape (num_gts, ).
|
||||
img_metas (list[dict]): List of image meta information.
|
||||
gt_bboxes_ignore_list (list[Tensor], optional): Bounding
|
||||
boxes which can be ignored for each image. Default None.
|
||||
|
||||
Returns:
|
||||
tuple: a tuple containing the following targets.
|
||||
|
||||
- labels_list (list[Tensor]): Labels for all images.
|
||||
- label_weights_list (list[Tensor]): Label weights for all \
|
||||
images.
|
||||
- bbox_targets_list (list[Tensor]): BBox targets for all \
|
||||
images.
|
||||
- bbox_weights_list (list[Tensor]): BBox weights for all \
|
||||
images.
|
||||
- num_total_pos (int): Number of positive samples in all \
|
||||
images.
|
||||
- num_total_neg (int): Number of negative samples in all \
|
||||
images.
|
||||
"""
|
||||
assert gt_bboxes_ignore_list is None, \
|
||||
'Only supports for gt_bboxes_ignore setting to None.'
|
||||
num_imgs = len(cls_scores_list)
|
||||
gt_bboxes_ignore_list = [
|
||||
gt_bboxes_ignore_list for _ in range(num_imgs)
|
||||
]
|
||||
|
||||
(labels_list, label_weights_list, bbox_targets_list,
|
||||
bbox_weights_list, pos_inds_list, neg_inds_list) = multi_apply(
|
||||
self._get_target_single, cls_scores_list, bbox_preds_list,
|
||||
gt_bboxes_list, gt_labels_list, img_metas, gt_bboxes_ignore_list)
|
||||
num_total_pos = sum((inds.numel() for inds in pos_inds_list))
|
||||
num_total_neg = sum((inds.numel() for inds in neg_inds_list))
|
||||
return (labels_list, label_weights_list, bbox_targets_list,
|
||||
bbox_weights_list, num_total_pos, num_total_neg)
|
||||
|
||||
def _get_area_thr(self, img_shape, type):
|
||||
MIN_V = 0
|
||||
MAX_V = 1e10
|
||||
short_edge = min(img_shape[0], img_shape[1])
|
||||
if type == 'v1':
|
||||
DELTA = 4
|
||||
if short_edge <= 600:
|
||||
min_edge = 128 - DELTA
|
||||
max_edge = MAX_V
|
||||
elif 600 < short_edge <= 800:
|
||||
min_edge = 96 - DELTA
|
||||
max_edge = MAX_V
|
||||
elif 800 < short_edge <= 1000:
|
||||
min_edge = 64 - DELTA
|
||||
max_edge = MAX_V
|
||||
elif 1000 < short_edge <= 1200:
|
||||
min_edge = 32 - DELTA
|
||||
max_edge = MAX_V
|
||||
elif 1200 < short_edge <= 1400:
|
||||
min_edge = MIN_V
|
||||
max_edge = MAX_V
|
||||
else:
|
||||
min_edge = MIN_V
|
||||
max_edge = 2 + DELTA
|
||||
elif type == 'v2':
|
||||
if short_edge <= 1000:
|
||||
min_edge = 112
|
||||
max_edge = MAX_V
|
||||
elif 1000 < short_edge <= 1400:
|
||||
min_edge = 32
|
||||
max_edge = 160
|
||||
elif short_edge > 1400:
|
||||
min_edge = 0
|
||||
max_edge = 80
|
||||
elif type == 'v3':
|
||||
if short_edge <= 800:
|
||||
min_edge = 96
|
||||
max_edge = MAX_V
|
||||
elif 800 < short_edge <= 1000:
|
||||
min_edge = 64
|
||||
max_edge = MAX_V
|
||||
elif 1000 < short_edge <= 1400:
|
||||
min_edge = MIN_V
|
||||
max_edge = MAX_V
|
||||
elif 1400 < short_edge <= 1600:
|
||||
min_edge = MIN_V
|
||||
max_edge = 96
|
||||
elif short_edge > 1600:
|
||||
min_edge = MIN_V
|
||||
max_edge = 64
|
||||
elif type == 'v4':
|
||||
DELTA = 4
|
||||
if short_edge <= 800:
|
||||
min_edge = 96 - DELTA
|
||||
max_edge = MAX_V
|
||||
elif 800 < short_edge <= 1000:
|
||||
min_edge = 64 - DELTA
|
||||
max_edge = MAX_V
|
||||
elif 1000 < short_edge <= 1400:
|
||||
min_edge = MIN_V
|
||||
max_edge = MAX_V
|
||||
elif 1400 < short_edge <= 1600:
|
||||
min_edge = MIN_V
|
||||
max_edge = 64 + DELTA
|
||||
elif short_edge > 1600:
|
||||
min_edge = MIN_V
|
||||
max_edge = 32 + DELTA
|
||||
|
||||
return min_edge ** 2, max_edge ** 2
|
||||
|
||||
def _get_target_single(self,
|
||||
cls_score,
|
||||
bbox_pred,
|
||||
gt_bboxes,
|
||||
gt_labels,
|
||||
img_meta,
|
||||
gt_bboxes_ignore=None):
|
||||
""""Compute regression and classification targets for one image.
|
||||
|
||||
Outputs from a single decoder layer of a single feature level are used.
|
||||
|
||||
Args:
|
||||
cls_score (Tensor): Box score logits from a single decoder layer
|
||||
for one image. Shape [num_query, cls_out_channels].
|
||||
bbox_pred (Tensor): Sigmoid outputs from a single decoder layer
|
||||
for one image, with normalized coordinate (cx, cy, w, h) and
|
||||
shape [num_query, 4].
|
||||
gt_bboxes (Tensor): Ground truth bboxes for one image with
|
||||
shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.
|
||||
gt_labels (Tensor): Ground truth class indices for one image
|
||||
with shape (num_gts, ).
|
||||
img_meta (dict): Meta information for one image.
|
||||
gt_bboxes_ignore (Tensor, optional): Bounding boxes
|
||||
which can be ignored. Default None.
|
||||
|
||||
Returns:
|
||||
tuple[Tensor]: a tuple containing the following for one image.
|
||||
|
||||
- labels (Tensor): Labels of each image.
|
||||
- label_weights (Tensor]): Label weights of each image.
|
||||
- bbox_targets (Tensor): BBox targets of each image.
|
||||
- bbox_weights (Tensor): BBox weights of each image.
|
||||
- pos_inds (Tensor): Sampled positive indices for each image.
|
||||
- neg_inds (Tensor): Sampled negative indices for each image.
|
||||
"""
|
||||
|
||||
num_bboxes = bbox_pred.size(0)
|
||||
# assigner and sampler
|
||||
assign_result = self.assigner.assign(bbox_pred, cls_score, gt_bboxes,
|
||||
gt_labels, img_meta,
|
||||
gt_bboxes_ignore)
|
||||
sampling_result = self.sampler.sample(assign_result, bbox_pred,
|
||||
gt_bboxes)
|
||||
pos_inds = sampling_result.pos_inds
|
||||
neg_inds = sampling_result.neg_inds
|
||||
|
||||
# label targets
|
||||
labels = gt_bboxes.new_full((num_bboxes, ),
|
||||
self.num_classes,
|
||||
dtype=torch.long)
|
||||
labels[pos_inds] = gt_labels[sampling_result.pos_assigned_gt_inds]
|
||||
label_weights = gt_bboxes.new_ones(num_bboxes)
|
||||
|
||||
# bbox targets
|
||||
bbox_targets = torch.zeros_like(bbox_pred)
|
||||
bbox_weights = torch.zeros_like(bbox_pred)
|
||||
bbox_weights[pos_inds] = 1.0
|
||||
img_h, img_w, _ = img_meta['img_shape']
|
||||
|
||||
# DETR regress the relative position of boxes (cxcywh) in the image.
|
||||
# Thus the learning target should be normalized by the image size, also
|
||||
# the box format should be converted from defaultly x1y1x2y2 to cxcywh.
|
||||
factor = bbox_pred.new_tensor([img_w, img_h, img_w,
|
||||
img_h]).unsqueeze(0)
|
||||
pos_gt_bboxes_normalized = sampling_result.pos_gt_bboxes / factor
|
||||
pos_gt_bboxes_targets = bbox_xyxy_to_cxcywh(pos_gt_bboxes_normalized)
|
||||
bbox_targets[pos_inds] = pos_gt_bboxes_targets
|
||||
return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,
|
||||
neg_inds)
|
||||
|
||||
# over-write because img_metas are needed as inputs for bbox_head.
|
||||
def forward_train(self,
|
||||
x,
|
||||
img_metas,
|
||||
gt_bboxes,
|
||||
gt_labels=None,
|
||||
gt_bboxes_ignore=None,
|
||||
proposal_cfg=None,
|
||||
**kwargs):
|
||||
"""Forward function for training mode.
|
||||
|
||||
Args:
|
||||
x (list[Tensor]): Features from backbone.
|
||||
img_metas (list[dict]): Meta information of each image, e.g.,
|
||||
image size, scaling factor, etc.
|
||||
gt_bboxes (Tensor): Ground truth bboxes of the image,
|
||||
shape (num_gts, 4).
|
||||
gt_labels (Tensor): Ground truth labels of each box,
|
||||
shape (num_gts,).
|
||||
gt_bboxes_ignore (Tensor): Ground truth bboxes to be
|
||||
ignored, shape (num_ignored_gts, 4).
|
||||
proposal_cfg (mmcv.Config): Test / postprocessing configuration,
|
||||
if None, test_cfg would be used.
|
||||
|
||||
Returns:
|
||||
dict[str, Tensor]: A dictionary of loss components.
|
||||
"""
|
||||
assert proposal_cfg is None, '"proposal_cfg" must be None'
|
||||
outs = self(x, img_metas)
|
||||
if gt_labels is None:
|
||||
loss_inputs = outs + (gt_bboxes, img_metas)
|
||||
else:
|
||||
loss_inputs = outs + (gt_bboxes, gt_labels, img_metas)
|
||||
losses = self.loss(*loss_inputs, gt_bboxes_ignore=gt_bboxes_ignore)
|
||||
return losses
|
||||
|
||||
@force_fp32(apply_to=('all_cls_scores_list', 'all_bbox_preds_list'))
|
||||
def get_bboxes(self,
|
||||
all_cls_scores_list,
|
||||
all_bbox_preds_list,
|
||||
img_metas,
|
||||
rescale=False):
|
||||
"""Transform network outputs for a batch into bbox predictions.
|
||||
|
||||
Args:
|
||||
all_cls_scores_list (list[Tensor]): Classification outputs
|
||||
for each feature level. Each is a 4D-tensor with shape
|
||||
[nb_dec, bs, num_query, cls_out_channels].
|
||||
all_bbox_preds_list (list[Tensor]): Sigmoid regression
|
||||
outputs for each feature level. Each is a 4D-tensor with
|
||||
normalized coordinate format (cx, cy, w, h) and shape
|
||||
[nb_dec, bs, num_query, 4].
|
||||
img_metas (list[dict]): Meta information of each image.
|
||||
rescale (bool, optional): If True, return boxes in original
|
||||
image space. Default False.
|
||||
|
||||
Returns:
|
||||
list[list[Tensor, Tensor]]: Each item in result_list is 2-tuple. \
|
||||
The first item is an (n, 5) tensor, where the first 4 columns \
|
||||
are bounding box positions (tl_x, tl_y, br_x, br_y) and the \
|
||||
5-th column is a score between 0 and 1. The second item is a \
|
||||
(n,) tensor where each item is the predicted class label of \
|
||||
the corresponding box.
|
||||
"""
|
||||
# NOTE defaultly only using outputs from the last feature level,
|
||||
# and only the outputs from the last decoder layer is used.
|
||||
cls_scores = all_cls_scores_list[-1][-1]
|
||||
bbox_preds = all_bbox_preds_list[-1][-1]
|
||||
|
||||
result_list = []
|
||||
for img_id in range(len(img_metas)):
|
||||
cls_score = cls_scores[img_id]
|
||||
bbox_pred = bbox_preds[img_id]
|
||||
img_shape = img_metas[img_id]['img_shape']
|
||||
scale_factor = img_metas[img_id]['scale_factor']
|
||||
proposals = self._get_bboxes_single(cls_score, bbox_pred,
|
||||
img_shape, scale_factor,
|
||||
rescale)
|
||||
result_list.append(proposals)
|
||||
|
||||
return result_list
|
||||
|
||||
def _get_bboxes_single(self,
|
||||
cls_score,
|
||||
bbox_pred,
|
||||
img_shape,
|
||||
scale_factor,
|
||||
rescale=False):
|
||||
"""Transform outputs from the last decoder layer into bbox predictions
|
||||
for each image.
|
||||
|
||||
Args:
|
||||
cls_score (Tensor): Box score logits from the last decoder layer
|
||||
for each image. Shape [num_query, cls_out_channels].
|
||||
bbox_pred (Tensor): Sigmoid outputs from the last decoder layer
|
||||
for each image, with coordinate format (cx, cy, w, h) and
|
||||
shape [num_query, 4].
|
||||
img_shape (tuple[int]): Shape of input image, (height, width, 3).
|
||||
scale_factor (ndarray, optional): Scale factor of the image arange
|
||||
as (w_scale, h_scale, w_scale, h_scale).
|
||||
rescale (bool, optional): If True, return boxes in original image
|
||||
space. Default False.
|
||||
|
||||
Returns:
|
||||
tuple[Tensor]: Results of detected bboxes and labels.
|
||||
|
||||
- det_bboxes: Predicted bboxes with shape [num_query, 5], \
|
||||
where the first 4 columns are bounding box positions \
|
||||
(tl_x, tl_y, br_x, br_y) and the 5-th column are scores \
|
||||
between 0 and 1.
|
||||
- det_labels: Predicted labels of the corresponding box with \
|
||||
shape [num_query].
|
||||
"""
|
||||
assert len(cls_score) == len(bbox_pred)
|
||||
max_per_img = self.test_cfg.get('max_per_img', self.num_query)
|
||||
# exclude background
|
||||
if self.loss_cls.use_sigmoid:
|
||||
cls_score = cls_score.sigmoid()
|
||||
scores, indexes = cls_score.view(-1).topk(max_per_img)
|
||||
det_labels = indexes % self.num_classes
|
||||
bbox_index = indexes // self.num_classes
|
||||
bbox_pred = bbox_pred[bbox_index]
|
||||
else:
|
||||
scores, det_labels = F.softmax(cls_score, dim=-1)[..., :-1].max(-1)
|
||||
scores, bbox_index = scores.topk(max_per_img)
|
||||
bbox_pred = bbox_pred[bbox_index]
|
||||
det_labels = det_labels[bbox_index]
|
||||
|
||||
det_bboxes = bbox_cxcywh_to_xyxy(bbox_pred)
|
||||
det_bboxes[:, 0::2] = det_bboxes[:, 0::2] * img_shape[1]
|
||||
det_bboxes[:, 1::2] = det_bboxes[:, 1::2] * img_shape[0]
|
||||
det_bboxes[:, 0::2].clamp_(min=0, max=img_shape[1])
|
||||
det_bboxes[:, 1::2].clamp_(min=0, max=img_shape[0])
|
||||
if rescale:
|
||||
det_bboxes /= det_bboxes.new_tensor(scale_factor)
|
||||
det_bboxes = torch.cat((det_bboxes, scores.unsqueeze(1)), -1)
|
||||
|
||||
return det_bboxes, det_labels
|
||||
|
||||
def simple_test_bboxes(self, feats, img_metas, rescale=False):
|
||||
"""Test det bboxes without test-time augmentation.
|
||||
|
||||
Args:
|
||||
feats (tuple[torch.Tensor]): Multi-level features from the
|
||||
upstream network, each is a 4D-tensor.
|
||||
img_metas (list[dict]): List of image information.
|
||||
rescale (bool, optional): Whether to rescale the results.
|
||||
Defaults to False.
|
||||
|
||||
Returns:
|
||||
list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple.
|
||||
The first item is ``bboxes`` with shape (n, 5),
|
||||
where 5 represent (tl_x, tl_y, br_x, br_y, score).
|
||||
The shape of the second tensor in the tuple is ``labels``
|
||||
with shape (n,)
|
||||
"""
|
||||
# forward of this head requires img_metas
|
||||
outs = self.forward(feats, img_metas)
|
||||
results_list = self.get_bboxes(*outs, img_metas, rescale=rescale)
|
||||
return results_list
|
||||
|
||||
def forward_onnx(self, feats, img_metas):
|
||||
"""Forward function for exporting to ONNX.
|
||||
|
||||
Over-write `forward` because: `masks` is directly created with
|
||||
zero (valid position tag) and has the same spatial size as `x`.
|
||||
Thus the construction of `masks` is different from that in `forward`.
|
||||
|
||||
Args:
|
||||
feats (tuple[Tensor]): Features from the upstream network, each is
|
||||
a 4D-tensor.
|
||||
img_metas (list[dict]): List of image information.
|
||||
|
||||
Returns:
|
||||
tuple[list[Tensor], list[Tensor]]: Outputs for all scale levels.
|
||||
|
||||
- all_cls_scores_list (list[Tensor]): Classification scores \
|
||||
for each scale level. Each is a 4D-tensor with shape \
|
||||
[nb_dec, bs, num_query, cls_out_channels]. Note \
|
||||
`cls_out_channels` should includes background.
|
||||
- all_bbox_preds_list (list[Tensor]): Sigmoid regression \
|
||||
outputs for each scale level. Each is a 4D-tensor with \
|
||||
normalized coordinate format (cx, cy, w, h) and shape \
|
||||
[nb_dec, bs, num_query, 4].
|
||||
"""
|
||||
num_levels = len(feats)
|
||||
img_metas_list = [img_metas for _ in range(num_levels)]
|
||||
return multi_apply(self.forward_single_onnx, feats, img_metas_list)
|
||||
|
||||
def forward_single_onnx(self, x, img_metas):
|
||||
""""Forward function for a single feature level with ONNX exportation.
|
||||
|
||||
Args:
|
||||
x (Tensor): Input feature from backbone's single stage, shape
|
||||
[bs, c, h, w].
|
||||
img_metas (list[dict]): List of image information.
|
||||
|
||||
Returns:
|
||||
all_cls_scores (Tensor): Outputs from the classification head,
|
||||
shape [nb_dec, bs, num_query, cls_out_channels]. Note
|
||||
cls_out_channels should includes background.
|
||||
all_bbox_preds (Tensor): Sigmoid outputs from the regression
|
||||
head with normalized coordinate format (cx, cy, w, h).
|
||||
Shape [nb_dec, bs, num_query, 4].
|
||||
"""
|
||||
# Note `img_shape` is not dynamically traceable to ONNX,
|
||||
# since the related augmentation was done with numpy under
|
||||
# CPU. Thus `masks` is directly created with zeros (valid tag)
|
||||
# and the same spatial shape as `x`.
|
||||
# The difference between torch and exported ONNX model may be
|
||||
# ignored, since the same performance is achieved (e.g.
|
||||
# 40.1 vs 40.1 for DETR)
|
||||
batch_size = x.size(0)
|
||||
h, w = x.size()[-2:]
|
||||
masks = x.new_zeros((batch_size, h, w)) # [B,h,w]
|
||||
|
||||
x = self.input_proj(x)
|
||||
# interpolate masks to have the same spatial shape with x
|
||||
masks = F.interpolate(
|
||||
masks.unsqueeze(1), size=x.shape[-2:]).to(torch.bool).squeeze(1)
|
||||
pos_embed = self.positional_encoding(masks)
|
||||
outs_dec, _ = self.transformer(x, masks, self.query_embedding.weight,
|
||||
pos_embed)
|
||||
|
||||
all_cls_scores = self.fc_cls(outs_dec)
|
||||
all_bbox_preds = self.fc_reg(self.activate(
|
||||
self.reg_ffn(outs_dec))).sigmoid()
|
||||
return all_cls_scores, all_bbox_preds
|
||||
|
||||
def onnx_export(self, all_cls_scores_list, all_bbox_preds_list, img_metas):
|
||||
"""Transform network outputs into bbox predictions, with ONNX
|
||||
exportation.
|
||||
|
||||
Args:
|
||||
all_cls_scores_list (list[Tensor]): Classification outputs
|
||||
for each feature level. Each is a 4D-tensor with shape
|
||||
[nb_dec, bs, num_query, cls_out_channels].
|
||||
all_bbox_preds_list (list[Tensor]): Sigmoid regression
|
||||
outputs for each feature level. Each is a 4D-tensor with
|
||||
normalized coordinate format (cx, cy, w, h) and shape
|
||||
[nb_dec, bs, num_query, 4].
|
||||
img_metas (list[dict]): Meta information of each image.
|
||||
|
||||
Returns:
|
||||
tuple[Tensor, Tensor]: dets of shape [N, num_det, 5]
|
||||
and class labels of shape [N, num_det].
|
||||
"""
|
||||
assert len(img_metas) == 1, \
|
||||
'Only support one input image while in exporting to ONNX'
|
||||
|
||||
cls_scores = all_cls_scores_list[-1][-1]
|
||||
bbox_preds = all_bbox_preds_list[-1][-1]
|
||||
|
||||
# Note `img_shape` is not dynamically traceable to ONNX,
|
||||
# here `img_shape_for_onnx` (padded shape of image tensor)
|
||||
# is used.
|
||||
img_shape = img_metas[0]['img_shape_for_onnx']
|
||||
max_per_img = self.test_cfg.get('max_per_img', self.num_query)
|
||||
batch_size = cls_scores.size(0)
|
||||
# `batch_index_offset` is used for the gather of concatenated tensor
|
||||
batch_index_offset = torch.arange(batch_size).to(
|
||||
cls_scores.device) * max_per_img
|
||||
batch_index_offset = batch_index_offset.unsqueeze(1).expand(
|
||||
batch_size, max_per_img)
|
||||
|
||||
# supports dynamical batch inference
|
||||
if self.loss_cls.use_sigmoid:
|
||||
cls_scores = cls_scores.sigmoid()
|
||||
scores, indexes = cls_scores.view(batch_size, -1).topk(
|
||||
max_per_img, dim=1)
|
||||
det_labels = indexes % self.num_classes
|
||||
bbox_index = indexes // self.num_classes
|
||||
bbox_index = (bbox_index + batch_index_offset).view(-1)
|
||||
bbox_preds = bbox_preds.view(-1, 4)[bbox_index]
|
||||
bbox_preds = bbox_preds.view(batch_size, -1, 4)
|
||||
else:
|
||||
scores, det_labels = F.softmax(
|
||||
cls_scores, dim=-1)[..., :-1].max(-1)
|
||||
scores, bbox_index = scores.topk(max_per_img, dim=1)
|
||||
bbox_index = (bbox_index + batch_index_offset).view(-1)
|
||||
bbox_preds = bbox_preds.view(-1, 4)[bbox_index]
|
||||
det_labels = det_labels.view(-1)[bbox_index]
|
||||
bbox_preds = bbox_preds.view(batch_size, -1, 4)
|
||||
det_labels = det_labels.view(batch_size, -1)
|
||||
|
||||
det_bboxes = bbox_cxcywh_to_xyxy(bbox_preds)
|
||||
# use `img_shape_tensor` for dynamically exporting to ONNX
|
||||
img_shape_tensor = img_shape.flip(0).repeat(2) # [w,h,w,h]
|
||||
img_shape_tensor = img_shape_tensor.unsqueeze(0).unsqueeze(0).expand(
|
||||
batch_size, det_bboxes.size(1), 4)
|
||||
det_bboxes = det_bboxes * img_shape_tensor
|
||||
# dynamically clip bboxes
|
||||
x1, y1, x2, y2 = det_bboxes.split((1, 1, 1, 1), dim=-1)
|
||||
from mmdet.core.export import dynamic_clip_for_onnx
|
||||
x1, y1, x2, y2 = dynamic_clip_for_onnx(x1, y1, x2, y2, img_shape)
|
||||
det_bboxes = torch.cat([x1, y1, x2, y2], dim=-1)
|
||||
det_bboxes = torch.cat((det_bboxes, scores.unsqueeze(-1)), -1)
|
||||
|
||||
return det_bboxes, det_labels
|
||||
365
detection/mmdet_custom/models/dense_heads/dino_head.py
Normal file
365
detection/mmdet_custom/models/dense_heads/dino_head.py
Normal file
@@ -0,0 +1,365 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
from mmdet.core import (bbox_cxcywh_to_xyxy, bbox_xyxy_to_cxcywh, multi_apply,
|
||||
reduce_mean)
|
||||
from ..utils import build_dn_generator
|
||||
from mmdet.models.utils.transformer import inverse_sigmoid
|
||||
from mmdet.models.builder import HEADS
|
||||
from .deformable_detr_head import DeformableDETRHead
|
||||
from mmcv.runner import force_fp32
|
||||
|
||||
|
||||
@HEADS.register_module()
|
||||
class DINOHead(DeformableDETRHead):
|
||||
|
||||
def __init__(self, *args, dn_cfg=None, **kwargs):
|
||||
super(DINOHead, self).__init__(*args, **kwargs)
|
||||
self._init_layers()
|
||||
self.init_denoising(dn_cfg)
|
||||
assert self.as_two_stage, \
|
||||
'as_two_stage must be True for DINO'
|
||||
assert self.with_box_refine, \
|
||||
'with_box_refine must be True for DINO'
|
||||
|
||||
def _init_layers(self):
|
||||
super()._init_layers()
|
||||
# NOTE The original repo of DINO set the num_embeddings 92 for coco,
|
||||
# 91 (0~90) of which represents target classes and the 92 (91)
|
||||
# indicates [Unknown] class. However, the embedding of unknown class
|
||||
# is not used in the original DINO
|
||||
self.label_embedding = nn.Embedding(self.cls_out_channels,
|
||||
self.embed_dims)
|
||||
|
||||
def init_denoising(self, dn_cfg):
|
||||
if dn_cfg is not None:
|
||||
dn_cfg['num_classes'] = self.num_classes
|
||||
dn_cfg['num_queries'] = self.num_query
|
||||
dn_cfg['hidden_dim'] = self.embed_dims
|
||||
self.dn_generator = build_dn_generator(dn_cfg)
|
||||
|
||||
def forward_train(self,
|
||||
x,
|
||||
img_metas,
|
||||
gt_bboxes,
|
||||
gt_labels=None,
|
||||
gt_bboxes_ignore=None,
|
||||
proposal_cfg=None,
|
||||
**kwargs):
|
||||
assert proposal_cfg is None, '"proposal_cfg" must be None'
|
||||
assert self.dn_generator is not None, '"dn_cfg" must be set'
|
||||
dn_label_query, dn_bbox_query, attn_mask, dn_meta = \
|
||||
self.dn_generator(gt_bboxes, gt_labels,
|
||||
self.label_embedding, img_metas)
|
||||
outs = self(x, img_metas, dn_label_query, dn_bbox_query, attn_mask)
|
||||
if gt_labels is None:
|
||||
loss_inputs = outs + (gt_bboxes, img_metas, dn_meta)
|
||||
else:
|
||||
loss_inputs = outs + (gt_bboxes, gt_labels, img_metas, dn_meta)
|
||||
losses = self.loss(*loss_inputs, gt_bboxes_ignore=gt_bboxes_ignore)
|
||||
return losses
|
||||
|
||||
def forward(self,
|
||||
mlvl_feats,
|
||||
img_metas,
|
||||
dn_label_query=None,
|
||||
dn_bbox_query=None,
|
||||
attn_mask=None):
|
||||
batch_size = mlvl_feats[0].size(0)
|
||||
input_img_h, input_img_w = img_metas[0]['batch_input_shape']
|
||||
img_masks = mlvl_feats[0].new_ones(
|
||||
(batch_size, input_img_h, input_img_w))
|
||||
for img_id in range(batch_size):
|
||||
if img_id >= len(img_metas): img_id = 0
|
||||
img_h, img_w, _ = img_metas[img_id]['img_shape']
|
||||
img_masks[img_id, :img_h, :img_w] = 0
|
||||
|
||||
mlvl_masks = []
|
||||
mlvl_positional_encodings = []
|
||||
for feat in mlvl_feats:
|
||||
mlvl_masks.append(
|
||||
F.interpolate(
|
||||
img_masks[None],
|
||||
size=feat.shape[-2:]).to(torch.bool).squeeze(0))
|
||||
mlvl_positional_encodings.append(
|
||||
self.positional_encoding(mlvl_masks[-1]))
|
||||
|
||||
query_embeds = None
|
||||
hs, inter_references, topk_score, topk_anchor = \
|
||||
self.transformer(
|
||||
mlvl_feats,
|
||||
mlvl_masks,
|
||||
query_embeds,
|
||||
mlvl_positional_encodings,
|
||||
dn_label_query,
|
||||
dn_bbox_query,
|
||||
attn_mask,
|
||||
reg_branches=self.reg_branches if self.with_box_refine else None, # noqa:E501
|
||||
cls_branches=self.cls_branches if self.as_two_stage else None # noqa:E501
|
||||
)
|
||||
hs = hs.permute(0, 2, 1, 3)
|
||||
|
||||
if dn_label_query is not None and dn_label_query.size(1) == 0:
|
||||
# NOTE: If there is no target in the image, the parameters of
|
||||
# label_embedding won't be used in producing loss, which raises
|
||||
# RuntimeError when using distributed mode.
|
||||
hs[0] += self.label_embedding.weight[0, 0] * 0.0
|
||||
|
||||
outputs_classes = []
|
||||
outputs_coords = []
|
||||
|
||||
for lvl in range(hs.shape[0]):
|
||||
reference = inter_references[lvl]
|
||||
reference = inverse_sigmoid(reference, eps=1e-3)
|
||||
outputs_class = self.cls_branches[lvl](hs[lvl])
|
||||
tmp = self.reg_branches[lvl](hs[lvl])
|
||||
if reference.shape[-1] == 4:
|
||||
tmp += reference
|
||||
else:
|
||||
assert reference.shape[-1] == 2
|
||||
tmp[..., :2] += reference
|
||||
outputs_coord = tmp.sigmoid()
|
||||
outputs_classes.append(outputs_class)
|
||||
outputs_coords.append(outputs_coord)
|
||||
|
||||
outputs_classes = torch.stack(outputs_classes)
|
||||
outputs_coords = torch.stack(outputs_coords)
|
||||
|
||||
return outputs_classes, outputs_coords, topk_score, topk_anchor
|
||||
|
||||
@force_fp32(apply_to=('all_cls_scores', 'all_bbox_preds'))
|
||||
def loss(self,
|
||||
all_cls_scores,
|
||||
all_bbox_preds,
|
||||
enc_topk_scores,
|
||||
enc_topk_anchors,
|
||||
gt_bboxes_list,
|
||||
gt_labels_list,
|
||||
img_metas,
|
||||
dn_meta=None,
|
||||
gt_bboxes_ignore=None):
|
||||
assert gt_bboxes_ignore is None, \
|
||||
f'{self.__class__.__name__} only supports ' \
|
||||
f'for gt_bboxes_ignore setting to None.'
|
||||
|
||||
loss_dict = dict()
|
||||
|
||||
# extract denoising and matching part of outputs
|
||||
all_cls_scores, all_bbox_preds, dn_cls_scores, dn_bbox_preds = \
|
||||
self.extract_dn_outputs(all_cls_scores, all_bbox_preds, dn_meta)
|
||||
|
||||
if enc_topk_scores is not None:
|
||||
# calculate loss from encode feature maps
|
||||
# NOTE The DeformDETR calculate binary cls loss
|
||||
# for all encoder embeddings, while DINO calculate
|
||||
# multi-class loss for topk embeddings.
|
||||
enc_loss_cls, enc_losses_bbox, enc_losses_iou = \
|
||||
self.loss_single(enc_topk_scores, enc_topk_anchors,
|
||||
gt_bboxes_list, gt_labels_list,
|
||||
img_metas, gt_bboxes_ignore)
|
||||
|
||||
# collate loss from encode feature maps
|
||||
loss_dict['interm_loss_cls'] = enc_loss_cls
|
||||
loss_dict['interm_loss_bbox'] = enc_losses_bbox
|
||||
loss_dict['interm_loss_iou'] = enc_losses_iou
|
||||
|
||||
# calculate loss from all decoder layers
|
||||
num_dec_layers = len(all_cls_scores)
|
||||
all_gt_bboxes_list = [gt_bboxes_list for _ in range(num_dec_layers)]
|
||||
all_gt_labels_list = [gt_labels_list for _ in range(num_dec_layers)]
|
||||
all_gt_bboxes_ignore_list = [
|
||||
gt_bboxes_ignore for _ in range(num_dec_layers)
|
||||
]
|
||||
img_metas_list = [img_metas for _ in range(num_dec_layers)]
|
||||
losses_cls, losses_bbox, losses_iou = multi_apply(
|
||||
self.loss_single, all_cls_scores, all_bbox_preds,
|
||||
all_gt_bboxes_list, all_gt_labels_list, img_metas_list,
|
||||
all_gt_bboxes_ignore_list)
|
||||
|
||||
# collate loss from the last decoder layer
|
||||
loss_dict['loss_cls'] = losses_cls[-1]
|
||||
loss_dict['loss_bbox'] = losses_bbox[-1]
|
||||
loss_dict['loss_iou'] = losses_iou[-1]
|
||||
|
||||
# collate loss from other decoder layers
|
||||
num_dec_layer = 0
|
||||
for loss_cls_i, loss_bbox_i, loss_iou_i in zip(losses_cls[:-1],
|
||||
losses_bbox[:-1],
|
||||
losses_iou[:-1]):
|
||||
loss_dict[f'd{num_dec_layer}.loss_cls'] = loss_cls_i
|
||||
loss_dict[f'd{num_dec_layer}.loss_bbox'] = loss_bbox_i
|
||||
loss_dict[f'd{num_dec_layer}.loss_iou'] = loss_iou_i
|
||||
num_dec_layer += 1
|
||||
|
||||
if dn_cls_scores is not None:
|
||||
# calculate denoising loss from all decoder layers
|
||||
dn_meta = [dn_meta for _ in img_metas]
|
||||
dn_losses_cls, dn_losses_bbox, dn_losses_iou = self.loss_dn(
|
||||
dn_cls_scores, dn_bbox_preds, gt_bboxes_list, gt_labels_list,
|
||||
img_metas, dn_meta)
|
||||
|
||||
# collate denoising loss
|
||||
loss_dict['dn_loss_cls'] = dn_losses_cls[-1]
|
||||
loss_dict['dn_loss_bbox'] = dn_losses_bbox[-1]
|
||||
loss_dict['dn_loss_iou'] = dn_losses_iou[-1]
|
||||
num_dec_layer = 0
|
||||
for loss_cls_i, loss_bbox_i, loss_iou_i in zip(
|
||||
dn_losses_cls[:-1], dn_losses_bbox[:-1],
|
||||
dn_losses_iou[:-1]):
|
||||
loss_dict[f'd{num_dec_layer}.dn_loss_cls'] = loss_cls_i
|
||||
loss_dict[f'd{num_dec_layer}.dn_loss_bbox'] = loss_bbox_i
|
||||
loss_dict[f'd{num_dec_layer}.dn_loss_iou'] = loss_iou_i
|
||||
num_dec_layer += 1
|
||||
|
||||
return loss_dict
|
||||
|
||||
def loss_dn(self, dn_cls_scores, dn_bbox_preds, gt_bboxes_list,
|
||||
gt_labels_list, img_metas, dn_meta):
|
||||
num_dec_layers = len(dn_cls_scores)
|
||||
all_gt_bboxes_list = [gt_bboxes_list for _ in range(num_dec_layers)]
|
||||
all_gt_labels_list = [gt_labels_list for _ in range(num_dec_layers)]
|
||||
img_metas_list = [img_metas for _ in range(num_dec_layers)]
|
||||
dn_meta_list = [dn_meta for _ in range(num_dec_layers)]
|
||||
return multi_apply(self.loss_dn_single, dn_cls_scores, dn_bbox_preds,
|
||||
all_gt_bboxes_list, all_gt_labels_list,
|
||||
img_metas_list, dn_meta_list)
|
||||
|
||||
def loss_dn_single(self, dn_cls_scores, dn_bbox_preds, gt_bboxes_list,
|
||||
gt_labels_list, img_metas, dn_meta):
|
||||
num_imgs = dn_cls_scores.size(0)
|
||||
bbox_preds_list = [dn_bbox_preds[i] for i in range(num_imgs)]
|
||||
cls_reg_targets = self.get_dn_target(bbox_preds_list, gt_bboxes_list,
|
||||
gt_labels_list, img_metas,
|
||||
dn_meta)
|
||||
(labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,
|
||||
num_total_pos, num_total_neg) = cls_reg_targets
|
||||
labels = torch.cat(labels_list, 0)
|
||||
label_weights = torch.cat(label_weights_list, 0)
|
||||
bbox_targets = torch.cat(bbox_targets_list, 0)
|
||||
bbox_weights = torch.cat(bbox_weights_list, 0)
|
||||
|
||||
# classification loss
|
||||
cls_scores = dn_cls_scores.reshape(-1, self.cls_out_channels)
|
||||
# construct weighted avg_factor to match with the official DETR repo
|
||||
cls_avg_factor = \
|
||||
num_total_pos * 1.0 + num_total_neg * self.bg_cls_weight
|
||||
if self.sync_cls_avg_factor:
|
||||
cls_avg_factor = reduce_mean(
|
||||
cls_scores.new_tensor([cls_avg_factor]))
|
||||
cls_avg_factor = max(cls_avg_factor, 1)
|
||||
|
||||
if len(cls_scores) > 0:
|
||||
loss_cls = self.loss_cls(
|
||||
cls_scores, labels, label_weights, avg_factor=cls_avg_factor)
|
||||
else:
|
||||
loss_cls = torch.zeros( # TODO: How to better return zero loss
|
||||
1,
|
||||
dtype=cls_scores.dtype,
|
||||
device=cls_scores.device)
|
||||
|
||||
# Compute the average number of gt boxes across all gpus, for
|
||||
# normalization purposes
|
||||
num_total_pos = loss_cls.new_tensor([num_total_pos])
|
||||
num_total_pos = torch.clamp(reduce_mean(num_total_pos), min=1).item()
|
||||
|
||||
# construct factors used for rescale bboxes
|
||||
factors = []
|
||||
for img_meta, bbox_pred in zip(img_metas, dn_bbox_preds):
|
||||
img_h, img_w, _ = img_meta['img_shape']
|
||||
factor = bbox_pred.new_tensor([img_w, img_h, img_w,
|
||||
img_h]).unsqueeze(0).repeat(
|
||||
bbox_pred.size(0), 1)
|
||||
factors.append(factor)
|
||||
factors = torch.cat(factors, 0)
|
||||
|
||||
# DETR regress the relative position of boxes (cxcywh) in the image,
|
||||
# thus the learning target is normalized by the image size. So here
|
||||
# we need to re-scale them for calculating IoU loss
|
||||
bbox_preds = dn_bbox_preds.reshape(-1, 4)
|
||||
bboxes = bbox_cxcywh_to_xyxy(bbox_preds) * factors
|
||||
bboxes_gt = bbox_cxcywh_to_xyxy(bbox_targets) * factors
|
||||
|
||||
# regression IoU loss, defaultly GIoU loss
|
||||
loss_iou = self.loss_iou(
|
||||
bboxes, bboxes_gt, bbox_weights, avg_factor=num_total_pos)
|
||||
|
||||
# regression L1 loss
|
||||
loss_bbox = self.loss_bbox(
|
||||
bbox_preds, bbox_targets, bbox_weights, avg_factor=num_total_pos)
|
||||
return loss_cls, loss_bbox, loss_iou
|
||||
|
||||
def get_dn_target(self, dn_bbox_preds_list, gt_bboxes_list, gt_labels_list,
|
||||
img_metas, dn_meta):
|
||||
(labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,
|
||||
pos_inds_list,
|
||||
neg_inds_list) = multi_apply(self._get_dn_target_single,
|
||||
dn_bbox_preds_list, gt_bboxes_list,
|
||||
gt_labels_list, img_metas, dn_meta)
|
||||
num_total_pos = sum((inds.numel() for inds in pos_inds_list))
|
||||
num_total_neg = sum((inds.numel() for inds in neg_inds_list))
|
||||
return (labels_list, label_weights_list, bbox_targets_list,
|
||||
bbox_weights_list, num_total_pos, num_total_neg)
|
||||
|
||||
def _get_dn_target_single(self, dn_bbox_pred, gt_bboxes, gt_labels,
|
||||
img_meta, dn_meta):
|
||||
num_groups = dn_meta['num_dn_group']
|
||||
pad_size = dn_meta['pad_size']
|
||||
assert pad_size % num_groups == 0
|
||||
single_pad = pad_size // num_groups
|
||||
num_bboxes = dn_bbox_pred.size(0)
|
||||
|
||||
if len(gt_labels) > 0:
|
||||
t = torch.range(0, len(gt_labels) - 1).long().cuda()
|
||||
t = t.unsqueeze(0).repeat(num_groups, 1)
|
||||
pos_assigned_gt_inds = t.flatten()
|
||||
pos_inds = (torch.tensor(range(num_groups)) *
|
||||
single_pad).long().cuda().unsqueeze(1) + t
|
||||
pos_inds = pos_inds.flatten()
|
||||
else:
|
||||
pos_inds = pos_assigned_gt_inds = torch.tensor([]).long().cuda()
|
||||
neg_inds = pos_inds + single_pad // 2
|
||||
|
||||
# label targets
|
||||
labels = gt_bboxes.new_full((num_bboxes, ),
|
||||
self.num_classes,
|
||||
dtype=torch.long)
|
||||
labels[pos_inds] = gt_labels[pos_assigned_gt_inds]
|
||||
label_weights = gt_bboxes.new_ones(num_bboxes)
|
||||
|
||||
# bbox targets
|
||||
bbox_targets = torch.zeros_like(dn_bbox_pred)
|
||||
bbox_weights = torch.zeros_like(dn_bbox_pred)
|
||||
bbox_weights[pos_inds] = 1.0
|
||||
img_h, img_w, _ = img_meta['img_shape']
|
||||
|
||||
# DETR regress the relative position of boxes (cxcywh) in the image.
|
||||
# Thus the learning target should be normalized by the image size, also
|
||||
# the box format should be converted from defaultly x1y1x2y2 to cxcywh.
|
||||
factor = dn_bbox_pred.new_tensor([img_w, img_h, img_w,
|
||||
img_h]).unsqueeze(0)
|
||||
gt_bboxes_normalized = gt_bboxes / factor
|
||||
gt_bboxes_targets = bbox_xyxy_to_cxcywh(gt_bboxes_normalized)
|
||||
bbox_targets[pos_inds] = gt_bboxes_targets.repeat([num_groups, 1])
|
||||
|
||||
return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,
|
||||
neg_inds)
|
||||
|
||||
@staticmethod
|
||||
def extract_dn_outputs(all_cls_scores, all_bbox_preds, dn_meta):
|
||||
# if dn_meta and dn_meta['pad_size'] > 0:
|
||||
if dn_meta is not None:
|
||||
denoising_cls_scores = all_cls_scores[:, :, :
|
||||
dn_meta['pad_size'], :]
|
||||
denoising_bbox_preds = all_bbox_preds[:, :, :
|
||||
dn_meta['pad_size'], :]
|
||||
matching_cls_scores = all_cls_scores[:, :, dn_meta['pad_size']:, :]
|
||||
matching_bbox_preds = all_bbox_preds[:, :, dn_meta['pad_size']:, :]
|
||||
else:
|
||||
denoising_cls_scores = None
|
||||
denoising_bbox_preds = None
|
||||
matching_cls_scores = all_cls_scores
|
||||
matching_bbox_preds = all_bbox_preds
|
||||
return (matching_cls_scores, matching_bbox_preds, denoising_cls_scores,
|
||||
denoising_bbox_preds)
|
||||
27
detection/mmdet_custom/models/dense_heads/mask_rcnn.py
Normal file
27
detection/mmdet_custom/models/dense_heads/mask_rcnn.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from mmdet.models.builder import DETECTORS
|
||||
from .two_stage import TwoStageDetector
|
||||
|
||||
|
||||
@DETECTORS.register_module()
|
||||
class MaskRCNN_(TwoStageDetector):
|
||||
"""Implementation of `Mask R-CNN <https://arxiv.org/abs/1703.06870>`_"""
|
||||
|
||||
def __init__(self,
|
||||
backbone,
|
||||
rpn_head,
|
||||
roi_head,
|
||||
train_cfg,
|
||||
test_cfg,
|
||||
neck=None,
|
||||
pretrained=None,
|
||||
init_cfg=None):
|
||||
super(MaskRCNN_, self).__init__(
|
||||
backbone=backbone,
|
||||
neck=neck,
|
||||
rpn_head=rpn_head,
|
||||
roi_head=roi_head,
|
||||
train_cfg=train_cfg,
|
||||
test_cfg=test_cfg,
|
||||
pretrained=pretrained,
|
||||
init_cfg=init_cfg)
|
||||
369
detection/mmdet_custom/models/dense_heads/msda.py
Normal file
369
detection/mmdet_custom/models/dense_heads/msda.py
Normal file
@@ -0,0 +1,369 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
import torch
|
||||
from torch.cuda.amp import custom_bwd, custom_fwd
|
||||
from torch.autograd.function import Function, once_differentiable
|
||||
from mmcv.utils import ext_loader
|
||||
ext_module = ext_loader.load_ext(
|
||||
'_ext', ['ms_deform_attn_backward', 'ms_deform_attn_forward'])
|
||||
|
||||
class MultiScaleDeformableAttnFunction_fp16(Function):
|
||||
|
||||
@staticmethod
|
||||
@custom_fwd(cast_inputs=torch.float16)
|
||||
def forward(ctx, value, value_spatial_shapes, value_level_start_index,
|
||||
sampling_locations, attention_weights, im2col_step):
|
||||
"""GPU version of multi-scale deformable attention.
|
||||
|
||||
Args:
|
||||
value (Tensor): The value has shape
|
||||
(bs, num_keys, mum_heads, embed_dims//num_heads)
|
||||
value_spatial_shapes (Tensor): Spatial shape of
|
||||
each feature map, has shape (num_levels, 2),
|
||||
last dimension 2 represent (h, w)
|
||||
sampling_locations (Tensor): The location of sampling points,
|
||||
has shape
|
||||
(bs ,num_queries, num_heads, num_levels, num_points, 2),
|
||||
the last dimension 2 represent (x, y).
|
||||
attention_weights (Tensor): The weight of sampling points used
|
||||
when calculate the attention, has shape
|
||||
(bs ,num_queries, num_heads, num_levels, num_points),
|
||||
im2col_step (Tensor): The step used in image to column.
|
||||
|
||||
Returns:
|
||||
Tensor: has shape (bs, num_queries, embed_dims)
|
||||
"""
|
||||
ctx.im2col_step = im2col_step
|
||||
output = ext_module.ms_deform_attn_forward(
|
||||
value,
|
||||
value_spatial_shapes,
|
||||
value_level_start_index,
|
||||
sampling_locations,
|
||||
attention_weights,
|
||||
im2col_step=ctx.im2col_step)
|
||||
ctx.save_for_backward(value, value_spatial_shapes,
|
||||
value_level_start_index, sampling_locations,
|
||||
attention_weights)
|
||||
return output
|
||||
|
||||
@staticmethod
|
||||
@once_differentiable
|
||||
@custom_bwd
|
||||
def backward(ctx, grad_output):
|
||||
"""GPU version of backward function.
|
||||
|
||||
Args:
|
||||
grad_output (Tensor): Gradient
|
||||
of output tensor of forward.
|
||||
|
||||
Returns:
|
||||
Tuple[Tensor]: Gradient
|
||||
of input tensors in forward.
|
||||
"""
|
||||
value, value_spatial_shapes, value_level_start_index, \
|
||||
sampling_locations, attention_weights = ctx.saved_tensors
|
||||
grad_value = torch.zeros_like(value)
|
||||
grad_sampling_loc = torch.zeros_like(sampling_locations)
|
||||
grad_attn_weight = torch.zeros_like(attention_weights)
|
||||
|
||||
ext_module.ms_deform_attn_backward(
|
||||
value,
|
||||
value_spatial_shapes,
|
||||
value_level_start_index,
|
||||
sampling_locations,
|
||||
attention_weights,
|
||||
grad_output.contiguous(),
|
||||
grad_value,
|
||||
grad_sampling_loc,
|
||||
grad_attn_weight,
|
||||
im2col_step=ctx.im2col_step)
|
||||
|
||||
return grad_value, None, None, \
|
||||
grad_sampling_loc, grad_attn_weight, None
|
||||
|
||||
|
||||
|
||||
shm_size_dict = {
|
||||
"8.0": 163000,
|
||||
"8.6": 99000,
|
||||
"8.7": 163000,
|
||||
"8.9": 99000,
|
||||
"9.0": 227000,
|
||||
"7.5": 64000,
|
||||
"7.0": 96000,
|
||||
}
|
||||
|
||||
cuda_capability = f"{torch.cuda.get_device_properties(0).major}.{torch.cuda.get_device_properties(0).minor}"
|
||||
|
||||
if cuda_capability not in shm_size_dict:
|
||||
raise NotImplementedError
|
||||
|
||||
shm_size_cap = shm_size_dict[cuda_capability]
|
||||
|
||||
|
||||
class MultiScaleDeformableAttnFunction_fp32_old(Function):
|
||||
|
||||
@staticmethod
|
||||
@custom_fwd(cast_inputs=torch.float32)
|
||||
def forward(ctx, value, value_spatial_shapes, value_level_start_index,
|
||||
sampling_locations, attention_weights, im2col_step):
|
||||
"""GPU version of multi-scale deformable attention.
|
||||
|
||||
Args:
|
||||
value (Tensor): The value has shape
|
||||
(bs, num_keys, mum_heads, embed_dims//num_heads)
|
||||
value_spatial_shapes (Tensor): Spatial shape of
|
||||
each feature map, has shape (num_levels, 2),
|
||||
last dimension 2 represent (h, w)
|
||||
sampling_locations (Tensor): The location of sampling points,
|
||||
has shape
|
||||
(bs ,num_queries, num_heads, num_levels, num_points, 2),
|
||||
the last dimension 2 represent (x, y).
|
||||
attention_weights (Tensor): The weight of sampling points used
|
||||
when calculate the attention, has shape
|
||||
(bs ,num_queries, num_heads, num_levels, num_points),
|
||||
im2col_step (Tensor): The step used in image to column.
|
||||
|
||||
Returns:
|
||||
Tensor: has shape (bs, num_queries, embed_dims)
|
||||
"""
|
||||
|
||||
ctx.im2col_step = im2col_step
|
||||
output = ext_module.ms_deform_attn_forward(
|
||||
value,
|
||||
value_spatial_shapes,
|
||||
value_level_start_index,
|
||||
sampling_locations,
|
||||
attention_weights,
|
||||
im2col_step=ctx.im2col_step)
|
||||
ctx.save_for_backward(value, value_spatial_shapes,
|
||||
value_level_start_index, sampling_locations,
|
||||
attention_weights)
|
||||
return output
|
||||
|
||||
@staticmethod
|
||||
@once_differentiable
|
||||
@custom_bwd
|
||||
def backward(ctx, grad_output):
|
||||
"""GPU version of backward function.
|
||||
|
||||
Args:
|
||||
grad_output (Tensor): Gradient
|
||||
of output tensor of forward.
|
||||
|
||||
Returns:
|
||||
Tuple[Tensor]: Gradient
|
||||
of input tensors in forward.
|
||||
"""
|
||||
value, value_spatial_shapes, value_level_start_index, \
|
||||
sampling_locations, attention_weights = ctx.saved_tensors
|
||||
grad_value = torch.zeros_like(value)
|
||||
grad_sampling_loc = torch.zeros_like(sampling_locations)
|
||||
grad_attn_weight = torch.zeros_like(attention_weights)
|
||||
|
||||
ext_module.ms_deform_attn_backward(
|
||||
value,
|
||||
value_spatial_shapes,
|
||||
value_level_start_index,
|
||||
sampling_locations,
|
||||
attention_weights,
|
||||
grad_output.contiguous(),
|
||||
grad_value,
|
||||
grad_sampling_loc,
|
||||
grad_attn_weight,
|
||||
im2col_step=ctx.im2col_step)
|
||||
|
||||
return grad_value, None, None, \
|
||||
grad_sampling_loc, grad_attn_weight, None
|
||||
|
||||
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import math
|
||||
import warnings
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from torch.autograd.function import Function, once_differentiable
|
||||
|
||||
from mmcv import deprecated_api_warning
|
||||
from mmcv.cnn import constant_init, xavier_init
|
||||
from mmcv.cnn.bricks.registry import ATTENTION
|
||||
from mmcv.runner import BaseModule
|
||||
|
||||
ext_module = ext_loader.load_ext(
|
||||
'_ext', ['ms_deform_attn_backward', 'ms_deform_attn_forward'])
|
||||
import functools
|
||||
import time
|
||||
from collections import defaultdict
|
||||
import torch
|
||||
from mmcv.ops import MultiScaleDeformableAttention
|
||||
@ATTENTION.register_module()
|
||||
class FlashMultiScaleDeformableAttention(MultiScaleDeformableAttention):
|
||||
"""An attention module used in Deformable-Detr.
|
||||
|
||||
`Deformable DETR: Deformable Transformers for End-to-End Object Detection.
|
||||
<https://arxiv.org/pdf/2010.04159.pdf>`_.
|
||||
|
||||
Args:
|
||||
embed_dims (int): The embedding dimension of Attention.
|
||||
Default: 256.
|
||||
num_heads (int): Parallel attention heads. Default: 64.
|
||||
num_levels (int): The number of feature map used in
|
||||
Attention. Default: 4.
|
||||
num_points (int): The number of sampling points for
|
||||
each query in each head. Default: 4.
|
||||
im2col_step (int): The step used in image_to_column.
|
||||
Default: 64.
|
||||
dropout (float): A Dropout layer on `inp_identity`.
|
||||
Default: 0.1.
|
||||
batch_first (bool): Key, Query and Value are shape of
|
||||
(batch, n, embed_dim)
|
||||
or (n, batch, embed_dim). Default to False.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: None.
|
||||
init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization.
|
||||
Default: None.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
use_flash=False,
|
||||
use_softmax=True,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.use_flash = use_flash
|
||||
self.use_softmax = use_softmax
|
||||
|
||||
@deprecated_api_warning({'residual': 'identity'},
|
||||
cls_name='FlashMultiScaleDeformableAttention')
|
||||
# @run_time('ms_attention')
|
||||
def forward(self,
|
||||
query,
|
||||
key=None,
|
||||
value=None,
|
||||
identity=None,
|
||||
query_pos=None,
|
||||
key_padding_mask=None,
|
||||
reference_points=None,
|
||||
spatial_shapes=None,
|
||||
level_start_index=None,
|
||||
**kwargs):
|
||||
"""Forward Function of MultiScaleDeformAttention.
|
||||
|
||||
Args:
|
||||
query (torch.Tensor): Query of Transformer with shape
|
||||
(num_query, bs, embed_dims).
|
||||
key (torch.Tensor): The key tensor with shape
|
||||
`(num_key, bs, embed_dims)`.
|
||||
value (torch.Tensor): The value tensor with shape
|
||||
`(num_key, bs, embed_dims)`.
|
||||
identity (torch.Tensor): The tensor used for addition, with the
|
||||
same shape as `query`. Default None. If None,
|
||||
`query` will be used.
|
||||
query_pos (torch.Tensor): The positional encoding for `query`.
|
||||
Default: None.
|
||||
key_pos (torch.Tensor): The positional encoding for `key`. Default
|
||||
None.
|
||||
reference_points (torch.Tensor): The normalized reference
|
||||
points with shape (bs, num_query, num_levels, 2),
|
||||
all elements is range in [0, 1], top-left (0,0),
|
||||
bottom-right (1, 1), including padding area.
|
||||
or (N, Length_{query}, num_levels, 4), add
|
||||
additional two dimensions is (w, h) to
|
||||
form reference boxes.
|
||||
key_padding_mask (torch.Tensor): ByteTensor for `query`, with
|
||||
shape [bs, num_key].
|
||||
spatial_shapes (torch.Tensor): Spatial shape of features in
|
||||
different levels. With shape (num_levels, 2),
|
||||
last dimension represents (h, w).
|
||||
level_start_index (torch.Tensor): The start index of each level.
|
||||
A tensor has shape ``(num_levels, )`` and can be represented
|
||||
as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].
|
||||
|
||||
Returns:
|
||||
torch.Tensor: forwarded results with shape
|
||||
[num_query, bs, embed_dims].
|
||||
"""
|
||||
|
||||
if value is None:
|
||||
value = query
|
||||
|
||||
if identity is None:
|
||||
identity = query
|
||||
if query_pos is not None:
|
||||
query = query + query_pos
|
||||
if not self.batch_first:
|
||||
# change to (bs, num_query ,embed_dims)
|
||||
query = query.permute(1, 0, 2)
|
||||
value = value.permute(1, 0, 2)
|
||||
|
||||
bs, num_query, _ = query.shape
|
||||
bs, num_value, _ = value.shape
|
||||
assert (spatial_shapes[:, 0] * spatial_shapes[:, 1]).sum() == num_value
|
||||
|
||||
value = self.value_proj(value)
|
||||
if key_padding_mask is not None:
|
||||
value = value.masked_fill(key_padding_mask[..., None], 0.0)
|
||||
value = value.view(bs, num_value, self.num_heads, -1)
|
||||
sampling_offsets = self.sampling_offsets(query).view(
|
||||
bs, num_query, self.num_heads, self.num_levels, self.num_points, 2)
|
||||
attention_weights = self.attention_weights(query).view(
|
||||
bs, num_query, self.num_heads, self.num_levels * self.num_points)
|
||||
|
||||
if not self.use_flash:
|
||||
if self.use_softmax:
|
||||
attention_weights = attention_weights.softmax(-1)
|
||||
attention_weights = attention_weights.view(bs, num_query,
|
||||
self.num_heads,
|
||||
self.num_levels,
|
||||
self.num_points)
|
||||
|
||||
else:
|
||||
attention_weights = attention_weights.view(bs, num_query,
|
||||
self.num_heads,
|
||||
self.num_levels,
|
||||
self.num_points, 1)
|
||||
|
||||
if reference_points.shape[-1] == 2:
|
||||
offset_normalizer = torch.stack(
|
||||
[spatial_shapes[..., 1], spatial_shapes[..., 0]], -1)
|
||||
sampling_locations = reference_points[:, :, None, :, None, :] \
|
||||
+ sampling_offsets \
|
||||
/ offset_normalizer[None, None, None, :, None, :]
|
||||
elif reference_points.shape[-1] == 4:
|
||||
sampling_locations = reference_points[:, :, None, :, None, :2] \
|
||||
+ sampling_offsets / self.num_points \
|
||||
* reference_points[:, :, None, :, None, 2:] \
|
||||
* 0.5
|
||||
else:
|
||||
raise ValueError(
|
||||
f'Last dim of reference_points must be'
|
||||
f' 2 or 4, but get {reference_points.shape[-1]} instead.')
|
||||
sampling_locations = sampling_locations.to(sampling_offsets.dtype)
|
||||
if torch.cuda.is_available() and value.is_cuda:
|
||||
if self.use_flash:
|
||||
assert False
|
||||
else:
|
||||
MultiScaleDeformableAttnFunction = MultiScaleDeformableAttnFunction_fp32_old
|
||||
|
||||
output = MultiScaleDeformableAttnFunction.apply(
|
||||
value, spatial_shapes, level_start_index, sampling_locations,
|
||||
attention_weights, self.im2col_step)
|
||||
|
||||
else:
|
||||
output = multi_scale_deformable_attn_pytorch(
|
||||
value, spatial_shapes, sampling_locations, attention_weights)
|
||||
|
||||
output = self.output_proj(output)
|
||||
|
||||
if not self.batch_first:
|
||||
# (num_query, bs ,embed_dims)
|
||||
output = output.permute(1, 0, 2)
|
||||
|
||||
return self.dropout(output) + identity
|
||||
225
detection/mmdet_custom/models/dense_heads/two_stage.py
Normal file
225
detection/mmdet_custom/models/dense_heads/two_stage.py
Normal file
@@ -0,0 +1,225 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import warnings
|
||||
|
||||
import torch
|
||||
|
||||
from mmdet.models.builder import DETECTORS, build_backbone, build_head, build_neck
|
||||
from mmdet.models.detectors.base import BaseDetector
|
||||
from mmcv.runner import BaseModule, force_fp32, auto_fp16
|
||||
import functools
|
||||
import time
|
||||
from collections import defaultdict
|
||||
import torch
|
||||
|
||||
|
||||
|
||||
# DETECTORS.register_module()
|
||||
class TwoStageDetector(BaseDetector):
|
||||
"""Base class for two-stage detectors.
|
||||
|
||||
Two-stage detectors typically consisting of a region proposal network and a
|
||||
task-specific regression head.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
backbone,
|
||||
neck=None,
|
||||
rpn_head=None,
|
||||
roi_head=None,
|
||||
train_cfg=None,
|
||||
test_cfg=None,
|
||||
pretrained=None,
|
||||
init_cfg=None):
|
||||
super(TwoStageDetector, self).__init__(init_cfg)
|
||||
if pretrained:
|
||||
warnings.warn('DeprecationWarning: pretrained is deprecated, '
|
||||
'please use "init_cfg" instead')
|
||||
backbone.pretrained = pretrained
|
||||
self.backbone = build_backbone(backbone)
|
||||
|
||||
if neck is not None:
|
||||
self.neck = build_neck(neck)
|
||||
|
||||
if rpn_head is not None:
|
||||
rpn_train_cfg = train_cfg.rpn if train_cfg is not None else None
|
||||
rpn_head_ = rpn_head.copy()
|
||||
rpn_head_.update(train_cfg=rpn_train_cfg, test_cfg=test_cfg.rpn)
|
||||
self.rpn_head = build_head(rpn_head_)
|
||||
|
||||
if roi_head is not None:
|
||||
# update train and test cfg here for now
|
||||
# TODO: refactor assigner & sampler
|
||||
rcnn_train_cfg = train_cfg.rcnn if train_cfg is not None else None
|
||||
roi_head.update(train_cfg=rcnn_train_cfg)
|
||||
roi_head.update(test_cfg=test_cfg.rcnn)
|
||||
roi_head.pretrained = pretrained
|
||||
self.roi_head = build_head(roi_head)
|
||||
|
||||
self.train_cfg = train_cfg
|
||||
self.test_cfg = test_cfg
|
||||
|
||||
@property
|
||||
def with_rpn(self):
|
||||
"""bool: whether the detector has RPN"""
|
||||
return hasattr(self, 'rpn_head') and self.rpn_head is not None
|
||||
|
||||
@property
|
||||
def with_roi_head(self):
|
||||
"""bool: whether the detector has a RoI head"""
|
||||
return hasattr(self, 'roi_head') and self.roi_head is not None
|
||||
|
||||
def use_backbone(self, img):
|
||||
return self.backbone(img)
|
||||
|
||||
# @auto_fp16(apply_to=('img',))
|
||||
def use_neck(self, img):
|
||||
# x = self.neck([each.float() for each in img])
|
||||
return self.neck(img)
|
||||
|
||||
def extract_feat(self, img):
|
||||
"""Directly extract features from the backbone+neck."""
|
||||
x = self.use_backbone(img)
|
||||
if self.with_neck:
|
||||
x = self.use_neck(x)
|
||||
return x
|
||||
|
||||
def forward_dummy(self, img):
|
||||
"""Used for computing network flops.
|
||||
|
||||
See `mmdetection/tools/analysis_tools/get_flops.py`
|
||||
"""
|
||||
outs = ()
|
||||
# backbone
|
||||
x = self.extract_feat(img)
|
||||
# rpn
|
||||
if self.with_rpn:
|
||||
rpn_outs = self.rpn_head(x)
|
||||
outs = outs + (rpn_outs, )
|
||||
proposals = torch.randn(1000, 4).to(img.device)
|
||||
# roi_head
|
||||
roi_outs = self.roi_head.forward_dummy(x, proposals)
|
||||
outs = outs + (roi_outs, )
|
||||
return outs
|
||||
|
||||
def forward_train(self,
|
||||
img,
|
||||
img_metas,
|
||||
gt_bboxes,
|
||||
gt_labels,
|
||||
gt_bboxes_ignore=None,
|
||||
gt_masks=None,
|
||||
proposals=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Args:
|
||||
img (Tensor): of shape (N, C, H, W) encoding input images.
|
||||
Typically these should be mean centered and std scaled.
|
||||
|
||||
img_metas (list[dict]): list of image info dict where each dict
|
||||
has: 'img_shape', 'scale_factor', 'flip', and may also contain
|
||||
'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.
|
||||
For details on the values of these keys see
|
||||
`mmdet/datasets/pipelines/formatting.py:Collect`.
|
||||
|
||||
gt_bboxes (list[Tensor]): Ground truth bboxes for each image with
|
||||
shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.
|
||||
|
||||
gt_labels (list[Tensor]): class indices corresponding to each box
|
||||
|
||||
gt_bboxes_ignore (None | list[Tensor]): specify which bounding
|
||||
boxes can be ignored when computing the loss.
|
||||
|
||||
gt_masks (None | Tensor) : true segmentation masks for each box
|
||||
used if the architecture supports a segmentation task.
|
||||
|
||||
proposals : override rpn proposals with custom proposals. Use when
|
||||
`with_rpn` is False.
|
||||
|
||||
Returns:
|
||||
dict[str, Tensor]: a dictionary of loss components
|
||||
"""
|
||||
x = self.extract_feat(img)
|
||||
|
||||
losses = dict()
|
||||
|
||||
# RPN forward and loss
|
||||
if self.with_rpn:
|
||||
proposal_cfg = self.train_cfg.get('rpn_proposal',
|
||||
self.test_cfg.rpn)
|
||||
rpn_losses, proposal_list = self.rpn_head.forward_train(
|
||||
x,
|
||||
img_metas,
|
||||
gt_bboxes,
|
||||
gt_labels=None,
|
||||
gt_bboxes_ignore=gt_bboxes_ignore,
|
||||
proposal_cfg=proposal_cfg,
|
||||
**kwargs)
|
||||
losses.update(rpn_losses)
|
||||
else:
|
||||
proposal_list = proposals
|
||||
|
||||
roi_losses = self.roi_head.forward_train(x, img_metas, proposal_list,
|
||||
gt_bboxes, gt_labels,
|
||||
gt_bboxes_ignore, gt_masks,
|
||||
**kwargs)
|
||||
losses.update(roi_losses)
|
||||
|
||||
return losses
|
||||
|
||||
async def async_simple_test(self,
|
||||
img,
|
||||
img_meta,
|
||||
proposals=None,
|
||||
rescale=False):
|
||||
"""Async test without augmentation."""
|
||||
assert self.with_bbox, 'Bbox head must be implemented.'
|
||||
x = self.extract_feat(img)
|
||||
|
||||
if proposals is None:
|
||||
proposal_list = await self.rpn_head.async_simple_test_rpn(
|
||||
x, img_meta)
|
||||
else:
|
||||
proposal_list = proposals
|
||||
|
||||
return await self.roi_head.async_simple_test(
|
||||
x, proposal_list, img_meta, rescale=rescale)
|
||||
|
||||
def simple_test(self, img, img_metas, proposals=None, rescale=False):
|
||||
"""Test without augmentation."""
|
||||
|
||||
assert self.with_bbox, 'Bbox head must be implemented.'
|
||||
x = self.extract_feat(img)
|
||||
if proposals is None:
|
||||
proposal_list = self.rpn_head.simple_test_rpn(x, img_metas)
|
||||
else:
|
||||
proposal_list = proposals
|
||||
|
||||
return self.roi_head.simple_test(
|
||||
x, proposal_list, img_metas, rescale=rescale)
|
||||
|
||||
def aug_test(self, imgs, img_metas, rescale=False):
|
||||
"""Test with augmentations.
|
||||
|
||||
If rescale is False, then returned bboxes and masks will fit the scale
|
||||
of imgs[0].
|
||||
"""
|
||||
x = self.extract_feats(imgs)
|
||||
proposal_list = self.rpn_head.aug_test_rpn(x, img_metas)
|
||||
return self.roi_head.aug_test(
|
||||
x, proposal_list, img_metas, rescale=rescale)
|
||||
|
||||
def onnx_export(self, img, img_metas):
|
||||
|
||||
img_shape = torch._shape_as_tensor(img)[2:]
|
||||
img_metas[0]['img_shape_for_onnx'] = img_shape
|
||||
x = self.extract_feat(img)
|
||||
proposals = self.rpn_head.onnx_export(x, img_metas)
|
||||
if hasattr(self.roi_head, 'onnx_export'):
|
||||
return self.roi_head.onnx_export(x, proposals, img_metas)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'{self.__class__.__name__} can not '
|
||||
f'be exported to ONNX. Please refer to the '
|
||||
f'list of supported models,'
|
||||
f'https://mmdetection.readthedocs.io/en/latest/tutorials/pytorch2onnx.html#list-of-supported-models-exportable-to-onnx' # noqa E501
|
||||
)
|
||||
9
detection/mmdet_custom/models/detectors/__init__.py
Normal file
9
detection/mmdet_custom/models/detectors/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from .dino import DINO
|
||||
|
||||
__all__ = ['DINO']
|
||||
10
detection/mmdet_custom/models/detectors/dino.py
Normal file
10
detection/mmdet_custom/models/detectors/dino.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from mmdet.models.builder import DETECTORS
|
||||
from mmdet.models.detectors.detr import DETR
|
||||
|
||||
|
||||
@DETECTORS.register_module()
|
||||
class DINO(DETR):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DETR, self).__init__(*args, **kwargs)
|
||||
207
detection/mmdet_custom/models/necks/fpn.py
Normal file
207
detection/mmdet_custom/models/necks/fpn.py
Normal file
@@ -0,0 +1,207 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from mmcv.cnn import ConvModule
|
||||
from mmcv.runner import BaseModule, auto_fp16
|
||||
from ..utils import ConvModule_Norm
|
||||
|
||||
from mmdet.models.builder import NECKS
|
||||
|
||||
|
||||
@NECKS.register_module()
|
||||
class FPN_vitdet(BaseModule):
|
||||
r"""Feature Pyramid Network.
|
||||
|
||||
This is an implementation of paper `Feature Pyramid Networks for Object
|
||||
Detection <https://arxiv.org/abs/1612.03144>`_.
|
||||
|
||||
Args:
|
||||
in_channels (List[int]): Number of input channels per scale.
|
||||
out_channels (int): Number of output channels (used at each scale)
|
||||
num_outs (int): Number of output scales.
|
||||
start_level (int): Index of the start input backbone level used to
|
||||
build the feature pyramid. Default: 0.
|
||||
end_level (int): Index of the end input backbone level (exclusive) to
|
||||
build the feature pyramid. Default: -1, which means the last level.
|
||||
add_extra_convs (bool | str): If bool, it decides whether to add conv
|
||||
layers on top of the original feature maps. Default to False.
|
||||
If True, it is equivalent to `add_extra_convs='on_input'`.
|
||||
If str, it specifies the source feature map of the extra convs.
|
||||
Only the following options are allowed
|
||||
|
||||
- 'on_input': Last feat map of neck inputs (i.e. backbone feature).
|
||||
- 'on_lateral': Last feature map after lateral convs.
|
||||
- 'on_output': The last output feature map after fpn convs.
|
||||
relu_before_extra_convs (bool): Whether to apply relu before the extra
|
||||
conv. Default: False.
|
||||
no_norm_on_lateral (bool): Whether to apply norm on lateral.
|
||||
Default: False.
|
||||
conv_cfg (dict): Config dict for convolution layer. Default: None.
|
||||
norm_cfg (dict): Config dict for normalization layer. Default: None.
|
||||
act_cfg (str): Config dict for activation layer in ConvModule.
|
||||
Default: None.
|
||||
upsample_cfg (dict): Config dict for interpolate layer.
|
||||
Default: `dict(mode='nearest')`
|
||||
init_cfg (dict or list[dict], optional): Initialization config dict.
|
||||
|
||||
Example:
|
||||
>>> import torch
|
||||
>>> in_channels = [2, 3, 5, 7]
|
||||
>>> scales = [340, 170, 84, 43]
|
||||
>>> inputs = [torch.rand(1, c, s, s)
|
||||
... for c, s in zip(in_channels, scales)]
|
||||
>>> self = FPN(in_channels, 11, len(in_channels)).eval()
|
||||
>>> outputs = self.forward(inputs)
|
||||
>>> for i in range(len(outputs)):
|
||||
... print(f'outputs[{i}].shape = {outputs[i].shape}')
|
||||
outputs[0].shape = torch.Size([1, 11, 340, 340])
|
||||
outputs[1].shape = torch.Size([1, 11, 170, 170])
|
||||
outputs[2].shape = torch.Size([1, 11, 84, 84])
|
||||
outputs[3].shape = torch.Size([1, 11, 43, 43])
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
num_outs,
|
||||
start_level=0,
|
||||
end_level=-1,
|
||||
add_extra_convs=False,
|
||||
relu_before_extra_convs=False,
|
||||
no_norm_on_lateral=False,
|
||||
conv_cfg=None,
|
||||
norm_cfg=None,
|
||||
act_cfg=None,
|
||||
use_residual=True,
|
||||
upsample_cfg=dict(mode='nearest'),
|
||||
init_cfg=dict(
|
||||
type='Xavier', layer='Conv2d', distribution='uniform')):
|
||||
super(FPN_vitdet, self).__init__(init_cfg)
|
||||
assert isinstance(in_channels, list)
|
||||
self.in_channels = in_channels
|
||||
self.out_channels = out_channels
|
||||
self.num_ins = len(in_channels)
|
||||
self.num_outs = num_outs
|
||||
self.relu_before_extra_convs = relu_before_extra_convs
|
||||
self.no_norm_on_lateral = no_norm_on_lateral
|
||||
self.fp16_enabled = False
|
||||
self.upsample_cfg = upsample_cfg.copy()
|
||||
self.use_residual = use_residual
|
||||
|
||||
if end_level == -1:
|
||||
self.backbone_end_level = self.num_ins
|
||||
assert num_outs >= self.num_ins - start_level
|
||||
else:
|
||||
# if end_level < inputs, no extra level is allowed
|
||||
self.backbone_end_level = end_level
|
||||
assert end_level <= len(in_channels)
|
||||
assert num_outs == end_level - start_level
|
||||
self.start_level = start_level
|
||||
self.end_level = end_level
|
||||
self.add_extra_convs = add_extra_convs
|
||||
assert isinstance(add_extra_convs, (str, bool))
|
||||
if isinstance(add_extra_convs, str):
|
||||
# Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output'
|
||||
assert add_extra_convs in ('on_input', 'on_lateral', 'on_output')
|
||||
elif add_extra_convs: # True
|
||||
self.add_extra_convs = 'on_input'
|
||||
|
||||
self.lateral_convs = nn.ModuleList()
|
||||
self.fpn_convs = nn.ModuleList()
|
||||
|
||||
for i in range(self.start_level, self.backbone_end_level):
|
||||
l_conv = ConvModule_Norm(
|
||||
in_channels[i],
|
||||
out_channels,
|
||||
1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg if not self.no_norm_on_lateral else None,
|
||||
act_cfg=act_cfg,
|
||||
inplace=False)
|
||||
fpn_conv = ConvModule_Norm(
|
||||
out_channels,
|
||||
out_channels,
|
||||
3,
|
||||
padding=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg,
|
||||
inplace=False)
|
||||
|
||||
self.lateral_convs.append(l_conv)
|
||||
self.fpn_convs.append(fpn_conv)
|
||||
|
||||
# add extra conv layers (e.g., RetinaNet)
|
||||
extra_levels = num_outs - self.backbone_end_level + self.start_level
|
||||
if self.add_extra_convs and extra_levels >= 1:
|
||||
for i in range(extra_levels):
|
||||
if i == 0 and self.add_extra_convs == 'on_input':
|
||||
in_channels = self.in_channels[self.backbone_end_level - 1]
|
||||
else:
|
||||
in_channels = out_channels
|
||||
extra_fpn_conv = ConvModule_Norm(
|
||||
in_channels,
|
||||
out_channels,
|
||||
3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg,
|
||||
inplace=False)
|
||||
self.fpn_convs.append(extra_fpn_conv)
|
||||
|
||||
@auto_fp16()
|
||||
def forward(self, inputs):
|
||||
"""Forward function."""
|
||||
assert len(inputs) == len(self.in_channels)
|
||||
|
||||
# build laterals
|
||||
laterals = [
|
||||
lateral_conv(inputs[i + self.start_level])
|
||||
for i, lateral_conv in enumerate(self.lateral_convs)
|
||||
]
|
||||
|
||||
# build top-down path
|
||||
used_backbone_levels = len(laterals)
|
||||
if self.use_residual:
|
||||
for i in range(used_backbone_levels - 1, 0, -1):
|
||||
# In some cases, fixing `scale factor` (e.g. 2) is preferred, but
|
||||
# it cannot co-exist with `size` in `F.interpolate`.
|
||||
if 'scale_factor' in self.upsample_cfg:
|
||||
laterals[i - 1] += F.interpolate(laterals[i],
|
||||
**self.upsample_cfg)
|
||||
else:
|
||||
prev_shape = laterals[i - 1].shape[2:]
|
||||
laterals[i - 1] += F.interpolate(
|
||||
laterals[i], size=prev_shape, **self.upsample_cfg)
|
||||
|
||||
# build outputs
|
||||
# part 1: from original levels
|
||||
outs = [
|
||||
self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels)
|
||||
]
|
||||
# part 2: add extra levels
|
||||
if self.num_outs > len(outs):
|
||||
# use max pool to get more levels on top of outputs
|
||||
# (e.g., Faster R-CNN, Mask R-CNN)
|
||||
if not self.add_extra_convs:
|
||||
for i in range(self.num_outs - used_backbone_levels):
|
||||
outs.append(F.max_pool2d(outs[-1], 1, stride=2))
|
||||
# add conv layers on top of original feature maps (RetinaNet)
|
||||
else:
|
||||
if self.add_extra_convs == 'on_input':
|
||||
extra_source = inputs[self.backbone_end_level - 1]
|
||||
elif self.add_extra_convs == 'on_lateral':
|
||||
extra_source = laterals[-1]
|
||||
elif self.add_extra_convs == 'on_output':
|
||||
extra_source = outs[-1]
|
||||
else:
|
||||
raise NotImplementedError
|
||||
outs.append(self.fpn_convs[used_backbone_levels](extra_source))
|
||||
for i in range(used_backbone_levels + 1, self.num_outs):
|
||||
if self.relu_before_extra_convs:
|
||||
outs.append(self.fpn_convs[i](F.relu(outs[-1])))
|
||||
else:
|
||||
outs.append(self.fpn_convs[i](outs[-1]))
|
||||
return tuple(outs)
|
||||
6
detection/mmdet_custom/models/utils/__init__.py
Normal file
6
detection/mmdet_custom/models/utils/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from .query_denoising import build_dn_generator
|
||||
from .transformer import (DinoTransformer, DinoTransformerDecoder)
|
||||
from .convModule_norm import ConvModule_Norm
|
||||
|
||||
|
||||
__all__ = ['build_dn_generator', 'DinoTransformer', 'DinoTransformerDecoder']
|
||||
34
detection/mmdet_custom/models/utils/convModule_norm.py
Normal file
34
detection/mmdet_custom/models/utils/convModule_norm.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from mmcv.cnn.bricks.conv_module import ConvModule
|
||||
|
||||
class ConvModule_Norm(ConvModule):
|
||||
def __init__(self, in_channels,
|
||||
out_channels,
|
||||
kernel, **kwargs):
|
||||
super().__init__(in_channels, out_channels, kernel, **kwargs)
|
||||
|
||||
self.normType = kwargs.get('norm_cfg', {'type':''})
|
||||
if self.normType is not None:
|
||||
self.normType = self.normType['type']
|
||||
|
||||
def forward(self, x, activate=True, norm=True):
|
||||
for layer in self.order:
|
||||
if layer == 'conv':
|
||||
if self.with_explicit_padding:
|
||||
x = self.padding_layer(x)
|
||||
x = self.conv(x)
|
||||
elif layer == 'norm' and norm and self.with_norm:
|
||||
if 'LN' in self.normType:
|
||||
x = x.permute(0, 2, 3, 1)
|
||||
x = self.norm(x)
|
||||
x = x.permute(0, 3, 1, 2).contiguous()
|
||||
else:
|
||||
x = self.norm(x)
|
||||
elif layer == 'act' and activate and self.with_activation:
|
||||
x = self.activate(x)
|
||||
return x
|
||||
234
detection/mmdet_custom/models/utils/query_denoising.py
Normal file
234
detection/mmdet_custom/models/utils/query_denoising.py
Normal file
@@ -0,0 +1,234 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch
|
||||
from mmcv.runner import BaseModule
|
||||
|
||||
from mmdet.core import bbox_xyxy_to_cxcywh
|
||||
from mmdet.models.utils.transformer import inverse_sigmoid
|
||||
|
||||
|
||||
class DnQueryGenerator(BaseModule):
|
||||
|
||||
def __init__(self,
|
||||
num_queries,
|
||||
hidden_dim,
|
||||
num_classes,
|
||||
noise_scale=dict(label=0.5, box=0.4),
|
||||
group_cfg=dict(
|
||||
dynamic=True, num_groups=None, num_dn_queries=None)):
|
||||
super(DnQueryGenerator, self).__init__()
|
||||
self.num_queries = num_queries
|
||||
self.hidden_dim = hidden_dim
|
||||
self.num_classes = num_classes
|
||||
self.label_noise_scale = noise_scale['label']
|
||||
self.box_noise_scale = noise_scale['box']
|
||||
self.dynamic_dn_groups = group_cfg.get('dynamic', False)
|
||||
if self.dynamic_dn_groups:
|
||||
assert 'num_dn_queries' in group_cfg, \
|
||||
'num_dn_queries should be set when using ' \
|
||||
'dynamic dn groups'
|
||||
self.num_dn = group_cfg['num_dn_queries']
|
||||
else:
|
||||
assert 'num_groups' in group_cfg, \
|
||||
'num_groups should be set when using ' \
|
||||
'static dn groups'
|
||||
self.num_dn = group_cfg['num_groups']
|
||||
assert isinstance(self.num_dn, int) and self.num_dn >= 1, \
|
||||
f'Expected the num in group_cfg to have type int. ' \
|
||||
f'Found {type(self.num_dn)} '
|
||||
|
||||
def get_num_groups(self, group_queries=None):
|
||||
"""
|
||||
Args:
|
||||
group_queries (int): Number of dn queries in one group.
|
||||
"""
|
||||
if self.dynamic_dn_groups:
|
||||
assert group_queries is not None, \
|
||||
'group_queries should be provided when using ' \
|
||||
'dynamic dn groups'
|
||||
if group_queries == 0:
|
||||
num_groups = 1
|
||||
else:
|
||||
num_groups = self.num_dn // group_queries
|
||||
else:
|
||||
num_groups = self.num_dn
|
||||
if num_groups < 1: # avoid num_groups < 1 in query generator
|
||||
num_groups = 1
|
||||
return int(num_groups)
|
||||
|
||||
def forward(self,
|
||||
gt_bboxes,
|
||||
gt_labels=None,
|
||||
label_enc=None,
|
||||
img_metas=None):
|
||||
"""
|
||||
Args:
|
||||
gt_bboxes (List[Tensor]): List of ground truth bboxes
|
||||
of the image, shape of each (num_gts, 4).
|
||||
gt_labels (List[Tensor]): List of ground truth labels
|
||||
of the image, shape of each (num_gts,), if None,
|
||||
TODO:noisy_label would be None.
|
||||
Returns:
|
||||
TODO
|
||||
"""
|
||||
# TODO: temp only support for CDN
|
||||
# TODO: temp assert gt_labels is not None and label_enc is not None
|
||||
|
||||
if self.training:
|
||||
if gt_labels is not None:
|
||||
assert len(gt_bboxes) == len(gt_labels), \
|
||||
f'the length of provided gt_labels ' \
|
||||
f'{len(gt_labels)} should be equal to' \
|
||||
f' that of gt_bboxes {len(gt_bboxes)}'
|
||||
assert gt_labels is not None \
|
||||
and label_enc is not None \
|
||||
and img_metas is not None # TODO: adjust args
|
||||
batch_size = len(gt_bboxes)
|
||||
|
||||
# convert bbox
|
||||
gt_bboxes_list = []
|
||||
for img_meta, bboxes in zip(img_metas, gt_bboxes):
|
||||
img_h, img_w, _ = img_meta['img_shape']
|
||||
factor = bboxes.new_tensor([img_w, img_h, img_w,
|
||||
img_h]).unsqueeze(0)
|
||||
bboxes_normalized = bbox_xyxy_to_cxcywh(bboxes) / factor
|
||||
gt_bboxes_list.append(bboxes_normalized)
|
||||
gt_bboxes = gt_bboxes_list
|
||||
|
||||
known = [torch.ones_like(labels) for labels in gt_labels]
|
||||
known_num = [sum(k) for k in known]
|
||||
|
||||
num_groups = self.get_num_groups(int(max(known_num)))
|
||||
|
||||
unmask_bbox = unmask_label = torch.cat(known)
|
||||
labels = torch.cat(gt_labels)
|
||||
boxes = torch.cat(gt_bboxes)
|
||||
batch_idx = torch.cat([
|
||||
torch.full_like(t.long(), i) for i, t in enumerate(gt_labels)
|
||||
])
|
||||
|
||||
known_indice = torch.nonzero(unmask_label + unmask_bbox)
|
||||
known_indice = known_indice.view(-1)
|
||||
|
||||
known_indice = known_indice.repeat(2 * num_groups, 1).view(-1)
|
||||
known_labels = labels.repeat(2 * num_groups, 1).view(-1)
|
||||
known_bid = batch_idx.repeat(2 * num_groups, 1).view(-1)
|
||||
known_bboxs = boxes.repeat(2 * num_groups, 1)
|
||||
known_labels_expand = known_labels.clone()
|
||||
known_bbox_expand = known_bboxs.clone()
|
||||
|
||||
if self.label_noise_scale > 0:
|
||||
p = torch.rand_like(known_labels_expand.float())
|
||||
chosen_indice = torch.nonzero(
|
||||
p < (self.label_noise_scale * 0.5)).view(-1)
|
||||
new_label = torch.randint_like(chosen_indice, 0,
|
||||
self.num_classes)
|
||||
known_labels_expand.scatter_(0, chosen_indice, new_label)
|
||||
single_pad = int(max(known_num)) # TODO
|
||||
|
||||
pad_size = int(single_pad * 2 * num_groups)
|
||||
positive_idx = torch.tensor(range(
|
||||
len(boxes))).long().cuda().unsqueeze(0).repeat(num_groups, 1)
|
||||
positive_idx += (torch.tensor(range(num_groups)) * len(boxes) *
|
||||
2).long().cuda().unsqueeze(1)
|
||||
positive_idx = positive_idx.flatten()
|
||||
negative_idx = positive_idx + len(boxes)
|
||||
if self.box_noise_scale > 0:
|
||||
known_bbox_ = torch.zeros_like(known_bboxs)
|
||||
known_bbox_[:, : 2] = \
|
||||
known_bboxs[:, : 2] - known_bboxs[:, 2:] / 2
|
||||
known_bbox_[:, 2:] = \
|
||||
known_bboxs[:, :2] + known_bboxs[:, 2:] / 2
|
||||
|
||||
diff = torch.zeros_like(known_bboxs)
|
||||
diff[:, :2] = known_bboxs[:, 2:] / 2
|
||||
diff[:, 2:] = known_bboxs[:, 2:] / 2
|
||||
|
||||
rand_sign = torch.randint_like(
|
||||
known_bboxs, low=0, high=2, dtype=torch.float32)
|
||||
rand_sign = rand_sign * 2.0 - 1.0
|
||||
rand_part = torch.rand_like(known_bboxs)
|
||||
rand_part[negative_idx] += 1.0
|
||||
rand_part *= rand_sign
|
||||
known_bbox_ += \
|
||||
torch.mul(rand_part, diff).cuda() * self.box_noise_scale
|
||||
known_bbox_ = known_bbox_.clamp(min=0.0, max=1.0)
|
||||
known_bbox_expand[:, :2] = \
|
||||
(known_bbox_[:, :2] + known_bbox_[:, 2:]) / 2
|
||||
known_bbox_expand[:, 2:] = \
|
||||
known_bbox_[:, 2:] - known_bbox_[:, :2]
|
||||
|
||||
m = known_labels_expand.long().to('cuda')
|
||||
input_label_embed = label_enc(m)
|
||||
input_bbox_embed = inverse_sigmoid(known_bbox_expand, eps=1e-3)
|
||||
|
||||
padding_label = torch.zeros(pad_size, self.hidden_dim).cuda()
|
||||
padding_bbox = torch.zeros(pad_size, 4).cuda()
|
||||
|
||||
input_query_label = padding_label.repeat(batch_size, 1, 1)
|
||||
input_query_bbox = padding_bbox.repeat(batch_size, 1, 1)
|
||||
|
||||
map_known_indice = torch.tensor([]).to('cuda')
|
||||
if len(known_num):
|
||||
map_known_indice = torch.cat(
|
||||
[torch.tensor(range(num)) for num in known_num])
|
||||
map_known_indice = torch.cat([
|
||||
map_known_indice + single_pad * i
|
||||
for i in range(2 * num_groups)
|
||||
]).long()
|
||||
if len(known_bid):
|
||||
input_query_label[(known_bid.long(),
|
||||
map_known_indice)] = input_label_embed
|
||||
input_query_bbox[(known_bid.long(),
|
||||
map_known_indice)] = input_bbox_embed
|
||||
|
||||
tgt_size = pad_size + self.num_queries
|
||||
attn_mask = torch.ones(tgt_size, tgt_size).to('cuda') < 0
|
||||
# match query cannot see the reconstruct
|
||||
attn_mask[pad_size:, :pad_size] = True
|
||||
# reconstruct cannot see each other
|
||||
for i in range(num_groups):
|
||||
if i == 0:
|
||||
attn_mask[single_pad * 2 * i:single_pad * 2 * (i + 1),
|
||||
single_pad * 2 * (i + 1):pad_size] = True
|
||||
if i == num_groups - 1:
|
||||
attn_mask[single_pad * 2 * i:single_pad * 2 *
|
||||
(i + 1), :single_pad * i * 2] = True
|
||||
else:
|
||||
attn_mask[single_pad * 2 * i:single_pad * 2 * (i + 1),
|
||||
single_pad * 2 * (i + 1):pad_size] = True
|
||||
attn_mask[single_pad * 2 * i:single_pad * 2 *
|
||||
(i + 1), :single_pad * 2 * i] = True
|
||||
|
||||
dn_meta = {
|
||||
'pad_size': pad_size,
|
||||
'num_dn_group': num_groups,
|
||||
}
|
||||
else:
|
||||
input_query_label = None
|
||||
input_query_bbox = None
|
||||
attn_mask = None
|
||||
dn_meta = None
|
||||
return input_query_label, input_query_bbox, attn_mask, dn_meta
|
||||
|
||||
|
||||
class CdnQueryGenerator(DnQueryGenerator):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CdnQueryGenerator, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def build_dn_generator(dn_args):
|
||||
"""
|
||||
Args:
|
||||
dn_args (dict):
|
||||
Returns:
|
||||
"""
|
||||
if dn_args is None:
|
||||
return None
|
||||
type = dn_args.pop('type')
|
||||
if type == 'DnQueryGenerator':
|
||||
return DnQueryGenerator(**dn_args)
|
||||
elif type == 'CdnQueryGenerator':
|
||||
return CdnQueryGenerator(**dn_args)
|
||||
else:
|
||||
raise NotImplementedError(f'{type} is not supported yet')
|
||||
278
detection/mmdet_custom/models/utils/transformer.py
Normal file
278
detection/mmdet_custom/models/utils/transformer.py
Normal file
@@ -0,0 +1,278 @@
|
||||
import math
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from mmdet.models.utils.builder import TRANSFORMER
|
||||
from mmcv.cnn.bricks.registry import (
|
||||
TRANSFORMER_LAYER_SEQUENCE, FEEDFORWARD_NETWORK, DROPOUT_LAYERS)
|
||||
from mmdet.models.utils.transformer import (inverse_sigmoid,
|
||||
DeformableDetrTransformerDecoder,
|
||||
DeformableDetrTransformer)
|
||||
|
||||
|
||||
def build_MLP(input_dim, hidden_dim, output_dim, num_layers):
|
||||
# TODO: It can be implemented by add an out_channel arg of
|
||||
# mmcv.cnn.bricks.transformer.FFN
|
||||
assert num_layers > 1, \
|
||||
f'num_layers should be greater than 1 but got {num_layers}'
|
||||
h = [hidden_dim] * (num_layers - 1)
|
||||
layers = list()
|
||||
for n, k in zip([input_dim] + h[:-1], h):
|
||||
layers.extend((nn.Linear(n, k), nn.ReLU()))
|
||||
# Note that the relu func of MLP in original DETR repo is set
|
||||
# 'inplace=False', however the ReLU cfg of FFN in mmdet is set
|
||||
# 'inplace=True' by default.
|
||||
layers.append(nn.Linear(hidden_dim, output_dim))
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
|
||||
@TRANSFORMER_LAYER_SEQUENCE.register_module()
|
||||
class DinoTransformerDecoder(DeformableDetrTransformerDecoder):
|
||||
|
||||
def __init__(self, *args, with_rp_noise=False, **kwargs):
|
||||
super(DinoTransformerDecoder, self).__init__(*args, **kwargs)
|
||||
self.with_rp_noise = with_rp_noise
|
||||
self._init_layers()
|
||||
|
||||
def _init_layers(self):
|
||||
self.ref_point_head = build_MLP(
|
||||
self.embed_dims * 2,
|
||||
self.embed_dims,
|
||||
self.embed_dims,
|
||||
2)
|
||||
self.norm = nn.LayerNorm(self.embed_dims)
|
||||
|
||||
# @staticmethod
|
||||
def gen_sineembed_for_position(self, pos_tensor):
|
||||
# n_query, bs, _ = pos_tensor.size()
|
||||
# sineembed_tensor = torch.zeros(n_query, bs, 256)
|
||||
scale = 2 * math.pi
|
||||
dim_t = torch.arange(
|
||||
self.embed_dims//2, dtype=torch.float32, device=pos_tensor.device)
|
||||
dim_t = 10000**(2 * (dim_t // 2) / (self.embed_dims//2))
|
||||
x_embed = pos_tensor[:, :, 0] * scale
|
||||
y_embed = pos_tensor[:, :, 1] * scale
|
||||
pos_x = x_embed[:, :, None] / dim_t
|
||||
pos_y = y_embed[:, :, None] / dim_t
|
||||
pos_x = torch.stack((pos_x[:, :, 0::2].sin(), pos_x[:, :, 1::2].cos()),
|
||||
dim=3).flatten(2)
|
||||
pos_y = torch.stack((pos_y[:, :, 0::2].sin(), pos_y[:, :, 1::2].cos()),
|
||||
dim=3).flatten(2)
|
||||
if pos_tensor.size(-1) == 2:
|
||||
pos = torch.cat((pos_y, pos_x), dim=2)
|
||||
elif pos_tensor.size(-1) == 4:
|
||||
w_embed = pos_tensor[:, :, 2] * scale
|
||||
pos_w = w_embed[:, :, None] / dim_t
|
||||
pos_w = torch.stack(
|
||||
(pos_w[:, :, 0::2].sin(), pos_w[:, :, 1::2].cos()),
|
||||
dim=3).flatten(2)
|
||||
|
||||
h_embed = pos_tensor[:, :, 3] * scale
|
||||
pos_h = h_embed[:, :, None] / dim_t
|
||||
pos_h = torch.stack(
|
||||
(pos_h[:, :, 0::2].sin(), pos_h[:, :, 1::2].cos()),
|
||||
dim=3).flatten(2)
|
||||
|
||||
pos = torch.cat((pos_y, pos_x, pos_w, pos_h), dim=2)
|
||||
else:
|
||||
raise ValueError('Unknown pos_tensor shape(-1):{}'.format(
|
||||
pos_tensor.size(-1)))
|
||||
return pos
|
||||
|
||||
def forward(self,
|
||||
query,
|
||||
*args,
|
||||
reference_points=None,
|
||||
valid_ratios=None,
|
||||
reg_branches=None,
|
||||
**kwargs):
|
||||
output = query
|
||||
intermediate = []
|
||||
intermediate_reference_points = [reference_points]
|
||||
for lid, layer in enumerate(self.layers):
|
||||
if reference_points.shape[-1] == 4:
|
||||
reference_points_input = \
|
||||
reference_points[:, :, None] * torch.cat(
|
||||
[valid_ratios, valid_ratios], -1)[:, None]
|
||||
else:
|
||||
assert reference_points.shape[-1] == 2
|
||||
reference_points_input = \
|
||||
reference_points[:, :, None] * valid_ratios[:, None]
|
||||
|
||||
if self.with_rp_noise and self.training:
|
||||
device = reference_points.device
|
||||
b, n, d = reference_points.size()
|
||||
noise = torch.rand(b, n, d).to(device) * 0.02 - 0.01
|
||||
reference_points = (reference_points + noise).clamp(0, 1)
|
||||
|
||||
query_sine_embed = self.gen_sineembed_for_position(
|
||||
reference_points_input[:, :, 0, :])
|
||||
query_pos = self.ref_point_head(query_sine_embed)
|
||||
|
||||
query_pos = query_pos.permute(1, 0, 2)
|
||||
output = layer(
|
||||
output,
|
||||
*args,
|
||||
query_pos=query_pos,
|
||||
reference_points=reference_points_input,
|
||||
**kwargs)
|
||||
output = output.permute(1, 0, 2)
|
||||
|
||||
if reg_branches is not None:
|
||||
tmp = reg_branches[lid](output)
|
||||
assert reference_points.shape[-1] == 4
|
||||
new_reference_points = tmp + inverse_sigmoid(
|
||||
reference_points, eps=1e-3)
|
||||
new_reference_points = new_reference_points.sigmoid()
|
||||
reference_points = new_reference_points.detach()
|
||||
|
||||
output = output.permute(1, 0, 2)
|
||||
if self.return_intermediate:
|
||||
intermediate.append(self.norm(output))
|
||||
intermediate_reference_points.append(new_reference_points)
|
||||
# NOTE this is for the "Look Forward Twice" module,
|
||||
# in the DeformDETR, reference_points was appended.
|
||||
|
||||
if self.return_intermediate:
|
||||
return torch.stack(intermediate), torch.stack(
|
||||
intermediate_reference_points)
|
||||
|
||||
return output, reference_points
|
||||
|
||||
|
||||
@TRANSFORMER.register_module()
|
||||
class DinoTransformer(DeformableDetrTransformer):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DinoTransformer, self).__init__(*args, **kwargs)
|
||||
|
||||
def init_layers(self):
|
||||
"""Initialize layers of the DinoTransformer."""
|
||||
self.level_embeds = nn.Parameter(
|
||||
torch.Tensor(self.num_feature_levels, self.embed_dims))
|
||||
self.enc_output = nn.Linear(self.embed_dims, self.embed_dims)
|
||||
self.enc_output_norm = nn.LayerNorm(self.embed_dims)
|
||||
self.query_embed = nn.Embedding(self.two_stage_num_proposals,
|
||||
self.embed_dims)
|
||||
|
||||
def init_weights(self):
|
||||
super().init_weights()
|
||||
nn.init.normal_(self.query_embed.weight.data)
|
||||
|
||||
def forward(self,
|
||||
mlvl_feats,
|
||||
mlvl_masks,
|
||||
query_embed,
|
||||
mlvl_pos_embeds,
|
||||
dn_label_query,
|
||||
dn_bbox_query,
|
||||
attn_mask,
|
||||
reg_branches=None,
|
||||
cls_branches=None,
|
||||
**kwargs):
|
||||
assert self.as_two_stage and query_embed is None, \
|
||||
'as_two_stage must be True for DINO'
|
||||
|
||||
feat_flatten = []
|
||||
mask_flatten = []
|
||||
lvl_pos_embed_flatten = []
|
||||
spatial_shapes = []
|
||||
for lvl, (feat, mask, pos_embed) in enumerate(
|
||||
zip(mlvl_feats, mlvl_masks, mlvl_pos_embeds)):
|
||||
bs, c, h, w = feat.shape
|
||||
spatial_shape = (h, w)
|
||||
spatial_shapes.append(spatial_shape)
|
||||
feat = feat.flatten(2).transpose(1, 2)
|
||||
mask = mask.flatten(1)
|
||||
pos_embed = pos_embed.flatten(2).transpose(1, 2)
|
||||
lvl_pos_embed = pos_embed + self.level_embeds[lvl].view(1, 1, -1)
|
||||
lvl_pos_embed_flatten.append(lvl_pos_embed)
|
||||
feat_flatten.append(feat)
|
||||
mask_flatten.append(mask)
|
||||
feat_flatten = torch.cat(feat_flatten, 1)
|
||||
mask_flatten = torch.cat(mask_flatten, 1)
|
||||
lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)
|
||||
spatial_shapes = torch.as_tensor(
|
||||
spatial_shapes, dtype=torch.long, device=feat_flatten.device)
|
||||
level_start_index = torch.cat((spatial_shapes.new_zeros(
|
||||
(1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))
|
||||
valid_ratios = torch.stack(
|
||||
[self.get_valid_ratio(m) for m in mlvl_masks], 1)
|
||||
|
||||
reference_points = self.get_reference_points(
|
||||
spatial_shapes, valid_ratios, device=feat.device)
|
||||
|
||||
feat_flatten = feat_flatten.permute(1, 0, 2) # (H*W, bs, embed_dims)
|
||||
lvl_pos_embed_flatten = lvl_pos_embed_flatten.permute(
|
||||
1, 0, 2) # (H*W, bs, embed_dims)
|
||||
memory = self.encoder(
|
||||
query=feat_flatten,
|
||||
key=None,
|
||||
value=None,
|
||||
query_pos=lvl_pos_embed_flatten,
|
||||
query_key_padding_mask=mask_flatten,
|
||||
spatial_shapes=spatial_shapes,
|
||||
reference_points=reference_points,
|
||||
level_start_index=level_start_index,
|
||||
valid_ratios=valid_ratios,
|
||||
**kwargs)
|
||||
|
||||
memory = memory.permute(1, 0, 2)
|
||||
bs, _, c = memory.shape
|
||||
|
||||
output_memory, output_proposals = self.gen_encoder_output_proposals(
|
||||
memory, mask_flatten, spatial_shapes)
|
||||
enc_outputs_class = cls_branches[self.decoder.num_layers](
|
||||
output_memory)
|
||||
enc_outputs_coord_unact = reg_branches[self.decoder.num_layers](
|
||||
output_memory) + output_proposals
|
||||
cls_out_features = cls_branches[self.decoder.num_layers].out_features
|
||||
topk = self.two_stage_num_proposals
|
||||
# NOTE In DeformDETR, enc_outputs_class[..., 0] is used for topk TODO
|
||||
topk_indices = torch.topk(enc_outputs_class.max(-1)[0], topk, dim=1)[1]
|
||||
# topk_proposal = torch.gather(
|
||||
# output_proposals, 1,
|
||||
# topk_indices.unsqueeze(-1).repeat(1, 1, 4)).sigmoid()
|
||||
# topk_memory = torch.gather(
|
||||
# output_memory, 1,
|
||||
# topk_indices.unsqueeze(-1).repeat(1, 1, self.embed_dims))
|
||||
topk_score = torch.gather(
|
||||
enc_outputs_class, 1,
|
||||
topk_indices.unsqueeze(-1).repeat(1, 1, cls_out_features))
|
||||
topk_coords_unact = torch.gather(
|
||||
enc_outputs_coord_unact, 1,
|
||||
topk_indices.unsqueeze(-1).repeat(1, 1, 4))
|
||||
topk_anchor = topk_coords_unact.sigmoid()
|
||||
# NOTE In the original DeformDETR, init_reference_out is obtained
|
||||
# from detached topk_coords_unact, which is different with DINO. TODO
|
||||
topk_coords_unact = topk_coords_unact.detach()
|
||||
|
||||
query = self.query_embed.weight[:, None, :].repeat(1, bs,
|
||||
1).transpose(0, 1)
|
||||
if dn_label_query is not None:
|
||||
query = torch.cat([dn_label_query, query], dim=1)
|
||||
if dn_bbox_query is not None:
|
||||
reference_points = torch.cat([dn_bbox_query, topk_coords_unact],
|
||||
dim=1)
|
||||
else:
|
||||
reference_points = topk_coords_unact
|
||||
reference_points = reference_points.sigmoid()
|
||||
|
||||
# decoder
|
||||
query = query.permute(1, 0, 2)
|
||||
memory = memory.permute(1, 0, 2)
|
||||
inter_states, inter_references = self.decoder(
|
||||
query=query,
|
||||
key=None,
|
||||
value=memory,
|
||||
attn_masks=attn_mask,
|
||||
key_padding_mask=mask_flatten,
|
||||
reference_points=reference_points,
|
||||
spatial_shapes=spatial_shapes,
|
||||
level_start_index=level_start_index,
|
||||
valid_ratios=valid_ratios,
|
||||
reg_branches=reg_branches,
|
||||
**kwargs)
|
||||
|
||||
inter_references_out = inter_references
|
||||
|
||||
return inter_states, inter_references_out, topk_score, topk_anchor
|
||||
7
detection/ops_dcnv3/functions/__init__.py
Normal file
7
detection/ops_dcnv3/functions/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from .dcnv3_func import DCNv3Function, dcnv3_core_pytorch
|
||||
188
detection/ops_dcnv3/functions/dcnv3_func.py
Normal file
188
detection/ops_dcnv3/functions/dcnv3_func.py
Normal file
@@ -0,0 +1,188 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import division
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from torch.autograd import Function
|
||||
from torch.autograd.function import once_differentiable
|
||||
from torch.cuda.amp import custom_bwd, custom_fwd
|
||||
import DCNv3
|
||||
|
||||
|
||||
class DCNv3Function(Function):
|
||||
@staticmethod
|
||||
@custom_fwd
|
||||
def forward(
|
||||
ctx, input, offset, mask,
|
||||
kernel_h, kernel_w, stride_h, stride_w,
|
||||
pad_h, pad_w, dilation_h, dilation_w,
|
||||
group, group_channels, offset_scale, im2col_step):
|
||||
ctx.kernel_h = kernel_h
|
||||
ctx.kernel_w = kernel_w
|
||||
ctx.stride_h = stride_h
|
||||
ctx.stride_w = stride_w
|
||||
ctx.pad_h = pad_h
|
||||
ctx.pad_w = pad_w
|
||||
ctx.dilation_h = dilation_h
|
||||
ctx.dilation_w = dilation_w
|
||||
ctx.group = group
|
||||
ctx.group_channels = group_channels
|
||||
ctx.offset_scale = offset_scale
|
||||
ctx.im2col_step = im2col_step
|
||||
output = DCNv3.dcnv3_forward(
|
||||
input, offset, mask, kernel_h,
|
||||
kernel_w, stride_h, stride_w, pad_h,
|
||||
pad_w, dilation_h, dilation_w, group,
|
||||
group_channels, offset_scale, ctx.im2col_step)
|
||||
ctx.save_for_backward(input, offset, mask)
|
||||
|
||||
return output
|
||||
|
||||
@staticmethod
|
||||
@once_differentiable
|
||||
@custom_bwd
|
||||
def backward(ctx, grad_output):
|
||||
input, offset, mask = ctx.saved_tensors
|
||||
grad_input, grad_offset, grad_mask = \
|
||||
DCNv3.dcnv3_backward(
|
||||
input, offset, mask, ctx.kernel_h,
|
||||
ctx.kernel_w, ctx.stride_h, ctx.stride_w, ctx.pad_h,
|
||||
ctx.pad_w, ctx.dilation_h, ctx.dilation_w, ctx.group,
|
||||
ctx.group_channels, ctx.offset_scale, grad_output.contiguous(), ctx.im2col_step)
|
||||
|
||||
return grad_input, grad_offset, grad_mask, \
|
||||
None, None, None, None, None, None, None, None, None, None, None, None
|
||||
|
||||
@staticmethod
|
||||
def symbolic(g, input, offset, mask, kernel_h, kernel_w, stride_h,
|
||||
stride_w, pad_h, pad_w, dilation_h, dilation_w, group,
|
||||
group_channels, offset_scale, im2col_step):
|
||||
"""Symbolic function for mmdeploy::DCNv3.
|
||||
|
||||
Returns:
|
||||
DCNv3 op for onnx.
|
||||
"""
|
||||
return g.op(
|
||||
'mmdeploy::TRTDCNv3',
|
||||
input,
|
||||
offset,
|
||||
mask,
|
||||
kernel_h_i=int(kernel_h),
|
||||
kernel_w_i=int(kernel_w),
|
||||
stride_h_i=int(stride_h),
|
||||
stride_w_i=int(stride_w),
|
||||
pad_h_i=int(pad_h),
|
||||
pad_w_i=int(pad_w),
|
||||
dilation_h_i=int(dilation_h),
|
||||
dilation_w_i=int(dilation_w),
|
||||
group_i=int(group),
|
||||
group_channels_i=int(group_channels),
|
||||
offset_scale_f=float(offset_scale),
|
||||
im2col_step_i=int(im2col_step),
|
||||
)
|
||||
|
||||
def _get_reference_points(spatial_shapes, device, kernel_h, kernel_w, dilation_h, dilation_w, pad_h=0, pad_w=0, stride_h=1, stride_w=1):
|
||||
_, H_, W_, _ = spatial_shapes
|
||||
H_out = (H_ - (dilation_h * (kernel_h - 1) + 1)) // stride_h + 1
|
||||
W_out = (W_ - (dilation_w * (kernel_w - 1) + 1)) // stride_w + 1
|
||||
|
||||
ref_y, ref_x = torch.meshgrid(
|
||||
torch.linspace(
|
||||
# pad_h + 0.5,
|
||||
# H_ - pad_h - 0.5,
|
||||
(dilation_h * (kernel_h - 1)) // 2 + 0.5,
|
||||
(dilation_h * (kernel_h - 1)) // 2 + 0.5 + (H_out - 1) * stride_h,
|
||||
H_out,
|
||||
dtype=torch.float32,
|
||||
device=device),
|
||||
torch.linspace(
|
||||
# pad_w + 0.5,
|
||||
# W_ - pad_w - 0.5,
|
||||
(dilation_w * (kernel_w - 1)) // 2 + 0.5,
|
||||
(dilation_w * (kernel_w - 1)) // 2 + 0.5 + (W_out - 1) * stride_w,
|
||||
W_out,
|
||||
dtype=torch.float32,
|
||||
device=device))
|
||||
ref_y = ref_y.reshape(-1)[None] / H_
|
||||
ref_x = ref_x.reshape(-1)[None] / W_
|
||||
|
||||
ref = torch.stack((ref_x, ref_y), -1).reshape(
|
||||
1, H_out, W_out, 1, 2)
|
||||
|
||||
return ref
|
||||
|
||||
|
||||
def _generate_dilation_grids(spatial_shapes, kernel_h, kernel_w, dilation_h, dilation_w, group, device):
|
||||
_, H_, W_, _ = spatial_shapes
|
||||
points_list = []
|
||||
x, y = torch.meshgrid(
|
||||
torch.linspace(
|
||||
-((dilation_w * (kernel_w - 1)) // 2),
|
||||
-((dilation_w * (kernel_w - 1)) // 2) +
|
||||
(kernel_w - 1) * dilation_w, kernel_w,
|
||||
dtype=torch.float32,
|
||||
device=device),
|
||||
torch.linspace(
|
||||
-((dilation_h * (kernel_h - 1)) // 2),
|
||||
-((dilation_h * (kernel_h - 1)) // 2) +
|
||||
(kernel_h - 1) * dilation_h, kernel_h,
|
||||
dtype=torch.float32,
|
||||
device=device))
|
||||
|
||||
points_list.extend([x / W_, y / H_])
|
||||
grid = torch.stack(points_list, -1).reshape(-1, 1, 2).\
|
||||
repeat(1, group, 1).permute(1, 0, 2)
|
||||
grid = grid.reshape(1, 1, 1, group * kernel_h * kernel_w, 2)
|
||||
|
||||
return grid
|
||||
|
||||
|
||||
def dcnv3_core_pytorch(
|
||||
input, offset, mask, kernel_h,
|
||||
kernel_w, stride_h, stride_w, pad_h,
|
||||
pad_w, dilation_h, dilation_w, group,
|
||||
group_channels, offset_scale):
|
||||
# for debug and test only,
|
||||
# need to use cuda version instead
|
||||
input = F.pad(
|
||||
input,
|
||||
[0, 0, pad_h, pad_h, pad_w, pad_w])
|
||||
N_, H_in, W_in, _ = input.shape
|
||||
_, H_out, W_out, _ = offset.shape
|
||||
|
||||
ref = _get_reference_points(
|
||||
input.shape, input.device, kernel_h, kernel_w, dilation_h, dilation_w, pad_h, pad_w, stride_h, stride_w)
|
||||
grid = _generate_dilation_grids(
|
||||
input.shape, kernel_h, kernel_w, dilation_h, dilation_w, group, input.device)
|
||||
spatial_norm = torch.tensor([W_in, H_in]).reshape(1, 1, 1, 2).\
|
||||
repeat(1, 1, 1, group*kernel_h*kernel_w).to(input.device)
|
||||
|
||||
sampling_locations = (ref + grid * offset_scale).repeat(N_, 1, 1, 1, 1).flatten(3, 4) + \
|
||||
offset * offset_scale / spatial_norm
|
||||
|
||||
P_ = kernel_h * kernel_w
|
||||
sampling_grids = 2 * sampling_locations - 1
|
||||
# N_, H_in, W_in, group*group_channels -> N_, H_in*W_in, group*group_channels -> N_, group*group_channels, H_in*W_in -> N_*group, group_channels, H_in, W_in
|
||||
input_ = input.view(N_, H_in*W_in, group*group_channels).transpose(1, 2).\
|
||||
reshape(N_*group, group_channels, H_in, W_in)
|
||||
# N_, H_out, W_out, group*P_*2 -> N_, H_out*W_out, group, P_, 2 -> N_, group, H_out*W_out, P_, 2 -> N_*group, H_out*W_out, P_, 2
|
||||
sampling_grid_ = sampling_grids.view(N_, H_out*W_out, group, P_, 2).transpose(1, 2).\
|
||||
flatten(0, 1)
|
||||
# N_*group, group_channels, H_out*W_out, P_
|
||||
sampling_input_ = F.grid_sample(
|
||||
input_, sampling_grid_, mode='bilinear', padding_mode='zeros', align_corners=False)
|
||||
|
||||
# (N_, H_out, W_out, group*P_) -> N_, H_out*W_out, group, P_ -> (N_, group, H_out*W_out, P_) -> (N_*group, 1, H_out*W_out, P_)
|
||||
mask = mask.view(N_, H_out*W_out, group, P_).transpose(1, 2).\
|
||||
reshape(N_*group, 1, H_out*W_out, P_)
|
||||
output = (sampling_input_ * mask).sum(-1).view(N_,
|
||||
group*group_channels, H_out*W_out)
|
||||
|
||||
return output.transpose(1, 2).reshape(N_, H_out, W_out, -1).contiguous()
|
||||
8
detection/ops_dcnv3/make.sh
Executable file
8
detection/ops_dcnv3/make.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
python setup.py build install
|
||||
7
detection/ops_dcnv3/modules/__init__.py
Normal file
7
detection/ops_dcnv3/modules/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from .dcnv3 import DCNv3, DCNv3_pytorch
|
||||
346
detection/ops_dcnv3/modules/dcnv3.py
Normal file
346
detection/ops_dcnv3/modules/dcnv3.py
Normal file
@@ -0,0 +1,346 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import division
|
||||
|
||||
import warnings
|
||||
import torch
|
||||
from torch import nn
|
||||
import torch.nn.functional as F
|
||||
from torch.nn.init import xavier_uniform_, constant_
|
||||
from ..functions import DCNv3Function, dcnv3_core_pytorch
|
||||
|
||||
|
||||
class to_channels_first(nn.Module):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def forward(self, x):
|
||||
return x.permute(0, 3, 1, 2)
|
||||
|
||||
|
||||
class to_channels_last(nn.Module):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def forward(self, x):
|
||||
return x.permute(0, 2, 3, 1)
|
||||
|
||||
|
||||
def build_norm_layer(dim,
|
||||
norm_layer,
|
||||
in_format='channels_last',
|
||||
out_format='channels_last',
|
||||
eps=1e-6):
|
||||
layers = []
|
||||
if norm_layer == 'BN':
|
||||
if in_format == 'channels_last':
|
||||
layers.append(to_channels_first())
|
||||
layers.append(nn.BatchNorm2d(dim))
|
||||
if out_format == 'channels_last':
|
||||
layers.append(to_channels_last())
|
||||
elif norm_layer == 'LN':
|
||||
if in_format == 'channels_first':
|
||||
layers.append(to_channels_last())
|
||||
layers.append(nn.LayerNorm(dim, eps=eps))
|
||||
if out_format == 'channels_first':
|
||||
layers.append(to_channels_first())
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'build_norm_layer does not support {norm_layer}')
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
|
||||
def build_act_layer(act_layer):
|
||||
if act_layer == 'ReLU':
|
||||
return nn.ReLU(inplace=True)
|
||||
elif act_layer == 'SiLU':
|
||||
return nn.SiLU(inplace=True)
|
||||
elif act_layer == 'GELU':
|
||||
return nn.GELU()
|
||||
|
||||
raise NotImplementedError(f'build_act_layer does not support {act_layer}')
|
||||
|
||||
|
||||
def _is_power_of_2(n):
|
||||
if (not isinstance(n, int)) or (n < 0):
|
||||
raise ValueError(
|
||||
"invalid input for _is_power_of_2: {} (type: {})".format(n, type(n)))
|
||||
|
||||
return (n & (n - 1) == 0) and n != 0
|
||||
|
||||
|
||||
class CenterFeatureScaleModule(nn.Module):
|
||||
def forward(self,
|
||||
query,
|
||||
center_feature_scale_proj_weight,
|
||||
center_feature_scale_proj_bias):
|
||||
center_feature_scale = F.linear(query,
|
||||
weight=center_feature_scale_proj_weight,
|
||||
bias=center_feature_scale_proj_bias).sigmoid()
|
||||
return center_feature_scale
|
||||
|
||||
|
||||
class DCNv3_pytorch(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
channels=64,
|
||||
kernel_size=3,
|
||||
dw_kernel_size=None,
|
||||
stride=1,
|
||||
pad=1,
|
||||
dilation=1,
|
||||
group=4,
|
||||
offset_scale=1.0,
|
||||
act_layer='GELU',
|
||||
norm_layer='LN',
|
||||
center_feature_scale=False):
|
||||
"""
|
||||
DCNv3 Module
|
||||
:param channels
|
||||
:param kernel_size
|
||||
:param stride
|
||||
:param pad
|
||||
:param dilation
|
||||
:param group
|
||||
:param offset_scale
|
||||
:param act_layer
|
||||
:param norm_layer
|
||||
"""
|
||||
super().__init__()
|
||||
if channels % group != 0:
|
||||
raise ValueError(
|
||||
f'channels must be divisible by group, but got {channels} and {group}')
|
||||
_d_per_group = channels // group
|
||||
dw_kernel_size = dw_kernel_size if dw_kernel_size is not None else kernel_size
|
||||
# you'd better set _d_per_group to a power of 2 which is more efficient in our CUDA implementation
|
||||
if not _is_power_of_2(_d_per_group):
|
||||
warnings.warn(
|
||||
"You'd better set channels in DCNv3 to make the dimension of each attention head a power of 2 "
|
||||
"which is more efficient in our CUDA implementation.")
|
||||
|
||||
self.offset_scale = offset_scale
|
||||
self.channels = channels
|
||||
self.kernel_size = kernel_size
|
||||
self.dw_kernel_size = dw_kernel_size
|
||||
self.stride = stride
|
||||
self.dilation = dilation
|
||||
self.pad = pad
|
||||
self.group = group
|
||||
self.group_channels = channels // group
|
||||
self.offset_scale = offset_scale
|
||||
self.center_feature_scale = center_feature_scale
|
||||
|
||||
self.dw_conv = nn.Sequential(
|
||||
nn.Conv2d(
|
||||
channels,
|
||||
channels,
|
||||
kernel_size=dw_kernel_size,
|
||||
stride=1,
|
||||
padding=(dw_kernel_size - 1) // 2,
|
||||
groups=channels),
|
||||
build_norm_layer(
|
||||
channels,
|
||||
norm_layer,
|
||||
'channels_first',
|
||||
'channels_last'),
|
||||
build_act_layer(act_layer))
|
||||
self.offset = nn.Linear(
|
||||
channels,
|
||||
group * kernel_size * kernel_size * 2)
|
||||
self.mask = nn.Linear(
|
||||
channels,
|
||||
group * kernel_size * kernel_size)
|
||||
self.input_proj = nn.Linear(channels, channels)
|
||||
self.output_proj = nn.Linear(channels, channels)
|
||||
self._reset_parameters()
|
||||
|
||||
if center_feature_scale:
|
||||
self.center_feature_scale_proj_weight = nn.Parameter(
|
||||
torch.zeros((group, channels), dtype=torch.float))
|
||||
self.center_feature_scale_proj_bias = nn.Parameter(
|
||||
torch.tensor(0.0, dtype=torch.float).view((1,)).repeat(group, ))
|
||||
self.center_feature_scale_module = CenterFeatureScaleModule()
|
||||
|
||||
def _reset_parameters(self):
|
||||
constant_(self.offset.weight.data, 0.)
|
||||
constant_(self.offset.bias.data, 0.)
|
||||
constant_(self.mask.weight.data, 0.)
|
||||
constant_(self.mask.bias.data, 0.)
|
||||
xavier_uniform_(self.input_proj.weight.data)
|
||||
constant_(self.input_proj.bias.data, 0.)
|
||||
xavier_uniform_(self.output_proj.weight.data)
|
||||
constant_(self.output_proj.bias.data, 0.)
|
||||
|
||||
def forward(self, input):
|
||||
"""
|
||||
:param query (N, H, W, C)
|
||||
:return output (N, H, W, C)
|
||||
"""
|
||||
N, H, W, _ = input.shape
|
||||
|
||||
x = self.input_proj(input)
|
||||
x_proj = x
|
||||
|
||||
x1 = input.permute(0, 3, 1, 2)
|
||||
x1 = self.dw_conv(x1)
|
||||
offset = self.offset(x1)
|
||||
mask = self.mask(x1).reshape(N, H, W, self.group, -1)
|
||||
mask = F.softmax(mask, -1).reshape(N, H, W, -1)
|
||||
|
||||
x = dcnv3_core_pytorch(
|
||||
x, offset, mask,
|
||||
self.kernel_size, self.kernel_size,
|
||||
self.stride, self.stride,
|
||||
self.pad, self.pad,
|
||||
self.dilation, self.dilation,
|
||||
self.group, self.group_channels,
|
||||
self.offset_scale)
|
||||
if self.center_feature_scale:
|
||||
center_feature_scale = self.center_feature_scale_module(
|
||||
x1, self.center_feature_scale_proj_weight, self.center_feature_scale_proj_bias)
|
||||
# N, H, W, groups -> N, H, W, groups, 1 -> N, H, W, groups, _d_per_group -> N, H, W, channels
|
||||
center_feature_scale = center_feature_scale[..., None].repeat(
|
||||
1, 1, 1, 1, self.channels // self.group).flatten(-2)
|
||||
x = x * (1 - center_feature_scale) + x_proj * center_feature_scale
|
||||
x = self.output_proj(x)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class DCNv3(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
channels=64,
|
||||
kernel_size=3,
|
||||
dw_kernel_size=None,
|
||||
stride=1,
|
||||
pad=1,
|
||||
dilation=1,
|
||||
group=4,
|
||||
offset_scale=1.0,
|
||||
act_layer='GELU',
|
||||
norm_layer='LN',
|
||||
center_feature_scale=False):
|
||||
"""
|
||||
DCNv3 Module
|
||||
:param channels
|
||||
:param kernel_size
|
||||
:param stride
|
||||
:param pad
|
||||
:param dilation
|
||||
:param group
|
||||
:param offset_scale
|
||||
:param act_layer
|
||||
:param norm_layer
|
||||
"""
|
||||
super().__init__()
|
||||
if channels % group != 0:
|
||||
raise ValueError(
|
||||
f'channels must be divisible by group, but got {channels} and {group}')
|
||||
_d_per_group = channels // group
|
||||
dw_kernel_size = dw_kernel_size if dw_kernel_size is not None else kernel_size
|
||||
# you'd better set _d_per_group to a power of 2 which is more efficient in our CUDA implementation
|
||||
if not _is_power_of_2(_d_per_group):
|
||||
warnings.warn(
|
||||
"You'd better set channels in DCNv3 to make the dimension of each attention head a power of 2 "
|
||||
"which is more efficient in our CUDA implementation.")
|
||||
|
||||
self.offset_scale = offset_scale
|
||||
self.channels = channels
|
||||
self.kernel_size = kernel_size
|
||||
self.dw_kernel_size = dw_kernel_size
|
||||
self.stride = stride
|
||||
self.dilation = dilation
|
||||
self.pad = pad
|
||||
self.group = group
|
||||
self.group_channels = channels // group
|
||||
self.offset_scale = offset_scale
|
||||
self.center_feature_scale = center_feature_scale
|
||||
|
||||
self.dw_conv = nn.Sequential(
|
||||
nn.Conv2d(
|
||||
channels,
|
||||
channels,
|
||||
kernel_size=dw_kernel_size,
|
||||
stride=1,
|
||||
padding=(dw_kernel_size - 1) // 2,
|
||||
groups=channels),
|
||||
build_norm_layer(
|
||||
channels,
|
||||
norm_layer,
|
||||
'channels_first',
|
||||
'channels_last'),
|
||||
build_act_layer(act_layer))
|
||||
self.offset = nn.Linear(
|
||||
channels,
|
||||
group * kernel_size * kernel_size * 2)
|
||||
self.mask = nn.Linear(
|
||||
channels,
|
||||
group * kernel_size * kernel_size)
|
||||
self.input_proj = nn.Linear(channels, channels)
|
||||
self.output_proj = nn.Linear(channels, channels)
|
||||
self._reset_parameters()
|
||||
|
||||
if center_feature_scale:
|
||||
self.center_feature_scale_proj_weight = nn.Parameter(
|
||||
torch.zeros((group, channels), dtype=torch.float))
|
||||
self.center_feature_scale_proj_bias = nn.Parameter(
|
||||
torch.tensor(0.0, dtype=torch.float).view((1,)).repeat(group, ))
|
||||
self.center_feature_scale_module = CenterFeatureScaleModule()
|
||||
|
||||
def _reset_parameters(self):
|
||||
constant_(self.offset.weight.data, 0.)
|
||||
constant_(self.offset.bias.data, 0.)
|
||||
constant_(self.mask.weight.data, 0.)
|
||||
constant_(self.mask.bias.data, 0.)
|
||||
xavier_uniform_(self.input_proj.weight.data)
|
||||
constant_(self.input_proj.bias.data, 0.)
|
||||
xavier_uniform_(self.output_proj.weight.data)
|
||||
constant_(self.output_proj.bias.data, 0.)
|
||||
|
||||
def forward(self, input):
|
||||
"""
|
||||
:param query (N, H, W, C)
|
||||
:return output (N, H, W, C)
|
||||
"""
|
||||
N, H, W, _ = input.shape
|
||||
|
||||
x = self.input_proj(input)
|
||||
x_proj = x
|
||||
dtype = x.dtype
|
||||
|
||||
x1 = input.permute(0, 3, 1, 2)
|
||||
x1 = self.dw_conv(x1)
|
||||
offset = self.offset(x1)
|
||||
mask = self.mask(x1).reshape(N, H, W, self.group, -1)
|
||||
mask = F.softmax(mask, -1).reshape(N, H, W, -1).type(dtype)
|
||||
|
||||
x = DCNv3Function.apply(
|
||||
x, offset, mask,
|
||||
self.kernel_size, self.kernel_size,
|
||||
self.stride, self.stride,
|
||||
self.pad, self.pad,
|
||||
self.dilation, self.dilation,
|
||||
self.group, self.group_channels,
|
||||
self.offset_scale,
|
||||
256)
|
||||
|
||||
if self.center_feature_scale:
|
||||
center_feature_scale = self.center_feature_scale_module(
|
||||
x1, self.center_feature_scale_proj_weight, self.center_feature_scale_proj_bias)
|
||||
# N, H, W, groups -> N, H, W, groups, 1 -> N, H, W, groups, _d_per_group -> N, H, W, channels
|
||||
center_feature_scale = center_feature_scale[..., None].repeat(
|
||||
1, 1, 1, 1, self.channels // self.group).flatten(-2)
|
||||
x = x * (1 - center_feature_scale) + x_proj * center_feature_scale
|
||||
x = self.output_proj(x)
|
||||
|
||||
return x
|
||||
75
detection/ops_dcnv3/setup.py
Normal file
75
detection/ops_dcnv3/setup.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
import os
|
||||
import glob
|
||||
|
||||
import torch
|
||||
|
||||
from torch.utils.cpp_extension import CUDA_HOME
|
||||
from torch.utils.cpp_extension import CppExtension
|
||||
from torch.utils.cpp_extension import CUDAExtension
|
||||
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
requirements = ["torch", "torchvision"]
|
||||
|
||||
|
||||
def get_extensions():
|
||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
extensions_dir = os.path.join(this_dir, "src")
|
||||
|
||||
main_file = glob.glob(os.path.join(extensions_dir, "*.cpp"))
|
||||
source_cpu = glob.glob(os.path.join(extensions_dir, "cpu", "*.cpp"))
|
||||
source_cuda = glob.glob(os.path.join(extensions_dir, "cuda", "*.cu"))
|
||||
|
||||
sources = main_file + source_cpu
|
||||
extension = CppExtension
|
||||
extra_compile_args = {"cxx": []}
|
||||
define_macros = []
|
||||
|
||||
if torch.cuda.is_available() and CUDA_HOME is not None:
|
||||
extension = CUDAExtension
|
||||
sources += source_cuda
|
||||
define_macros += [("WITH_CUDA", None)]
|
||||
extra_compile_args["nvcc"] = [
|
||||
# "-DCUDA_HAS_FP16=1",
|
||||
# "-D__CUDA_NO_HALF_OPERATORS__",
|
||||
# "-D__CUDA_NO_HALF_CONVERSIONS__",
|
||||
# "-D__CUDA_NO_HALF2_OPERATORS__",
|
||||
]
|
||||
else:
|
||||
raise NotImplementedError('Cuda is not availabel')
|
||||
|
||||
sources = [os.path.join(extensions_dir, s) for s in sources]
|
||||
include_dirs = [extensions_dir]
|
||||
ext_modules = [
|
||||
extension(
|
||||
"DCNv3",
|
||||
sources,
|
||||
include_dirs=include_dirs,
|
||||
define_macros=define_macros,
|
||||
extra_compile_args=extra_compile_args,
|
||||
)
|
||||
]
|
||||
return ext_modules
|
||||
|
||||
|
||||
setup(
|
||||
name="DCNv3",
|
||||
version="1.0",
|
||||
author="InternImage",
|
||||
url="https://github.com/OpenGVLab/InternImage",
|
||||
description=
|
||||
"PyTorch Wrapper for CUDA Functions of DCNv3",
|
||||
packages=find_packages(exclude=(
|
||||
"configs",
|
||||
"tests",
|
||||
)),
|
||||
ext_modules=get_extensions(),
|
||||
cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension},
|
||||
)
|
||||
37
detection/ops_dcnv3/src/cpu/dcnv3_cpu.cpp
Normal file
37
detection/ops_dcnv3/src/cpu/dcnv3_cpu.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*!
|
||||
**************************************************************************************************
|
||||
* InternImage
|
||||
* Copyright (c) 2022 OpenGVLab
|
||||
* Licensed under The MIT License [see LICENSE for details]
|
||||
**************************************************************************************************
|
||||
* Modified from
|
||||
*https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0
|
||||
**************************************************************************************************
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <ATen/ATen.h>
|
||||
#include <ATen/cuda/CUDAContext.h>
|
||||
|
||||
at::Tensor dcnv3_cpu_forward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h,
|
||||
const int stride_w, const int pad_h,
|
||||
const int pad_w, const int dilation_h,
|
||||
const int dilation_w, const int group,
|
||||
const int group_channels, const float offset_scale,
|
||||
const int im2col_step) {
|
||||
AT_ERROR("Not implement on cpu");
|
||||
}
|
||||
|
||||
std::vector<at::Tensor>
|
||||
dcnv3_cpu_backward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h, const int stride_w,
|
||||
const int pad_h, const int pad_w, const int dilation_h,
|
||||
const int dilation_w, const int group,
|
||||
const int group_channels, const float offset_scale,
|
||||
const at::Tensor &grad_output, const int im2col_step) {
|
||||
AT_ERROR("Not implement on cpu");
|
||||
}
|
||||
31
detection/ops_dcnv3/src/cpu/dcnv3_cpu.h
Normal file
31
detection/ops_dcnv3/src/cpu/dcnv3_cpu.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
**************************************************************************************************
|
||||
* InternImage
|
||||
* Copyright (c) 2022 OpenGVLab
|
||||
* Licensed under The MIT License [see LICENSE for details]
|
||||
**************************************************************************************************
|
||||
* Modified from
|
||||
*https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0
|
||||
**************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <torch/extension.h>
|
||||
|
||||
at::Tensor dcnv3_cpu_forward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h,
|
||||
const int stride_w, const int pad_h,
|
||||
const int pad_w, const int dilation_h,
|
||||
const int dilation_w, const int group,
|
||||
const int group_channels, const float offset_scale,
|
||||
const int im2col_step);
|
||||
|
||||
std::vector<at::Tensor>
|
||||
dcnv3_cpu_backward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h, const int stride_w,
|
||||
const int pad_h, const int pad_w, const int dilation_h,
|
||||
const int dilation_w, const int group,
|
||||
const int group_channels, const float offset_scale,
|
||||
const at::Tensor &grad_output, const int im2col_step);
|
||||
174
detection/ops_dcnv3/src/cuda/dcnv3_cuda.cu
Normal file
174
detection/ops_dcnv3/src/cuda/dcnv3_cuda.cu
Normal file
@@ -0,0 +1,174 @@
|
||||
/*!
|
||||
**************************************************************************************************
|
||||
* InternImage
|
||||
* Copyright (c) 2022 OpenGVLab
|
||||
* Licensed under The MIT License [see LICENSE for details]
|
||||
**************************************************************************************************
|
||||
* Modified from
|
||||
*https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0
|
||||
**************************************************************************************************
|
||||
*/
|
||||
|
||||
#include "cuda/dcnv3_im2col_cuda.cuh"
|
||||
#include <vector>
|
||||
|
||||
#include <ATen/ATen.h>
|
||||
#include <ATen/cuda/CUDAContext.h>
|
||||
#include <cuda.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <torch/torch.h>
|
||||
|
||||
at::Tensor dcnv3_cuda_forward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h,
|
||||
const int stride_w, const int pad_h,
|
||||
const int pad_w, const int dilation_h,
|
||||
const int dilation_w, const int group,
|
||||
const int group_channels,
|
||||
const float offset_scale, const int im2col_step) {
|
||||
AT_ASSERTM(input.is_contiguous(), "input tensor has to be contiguous");
|
||||
AT_ASSERTM(offset.is_contiguous(), "offset tensor has to be contiguous");
|
||||
AT_ASSERTM(mask.is_contiguous(), "mask tensor has to be contiguous");
|
||||
AT_ASSERTM(input.type().is_cuda(), "input must be a CUDA tensor");
|
||||
AT_ASSERTM(offset.type().is_cuda(), "offset must be a CUDA tensor");
|
||||
AT_ASSERTM(mask.type().is_cuda(), "mask must be a CUDA tensor");
|
||||
|
||||
const int batch = input.size(0);
|
||||
const int height_in = input.size(1);
|
||||
const int width_in = input.size(2);
|
||||
const int channels = input.size(3);
|
||||
const int height_out =
|
||||
(height_in + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h +
|
||||
1;
|
||||
const int width_out =
|
||||
(width_in + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w +
|
||||
1;
|
||||
const int im2col_step_ = std::min(batch, im2col_step);
|
||||
|
||||
AT_ASSERTM(batch % im2col_step_ == 0,
|
||||
"batch(%d) must divide im2col_step(%d)", batch, im2col_step_);
|
||||
AT_ASSERTM(
|
||||
channels == (group * group_channels),
|
||||
"Input channels and group times group channels wont match: (%d vs %d).",
|
||||
channels, group * group_channels);
|
||||
|
||||
auto output =
|
||||
at::zeros({batch, height_out, width_out, group * group_channels},
|
||||
input.options());
|
||||
|
||||
const int batch_n = im2col_step_;
|
||||
auto output_n = output.view({batch / batch_n, batch_n, height_out,
|
||||
width_out, group * group_channels});
|
||||
auto per_input_size = height_in * width_in * group * group_channels;
|
||||
auto per_offset_size =
|
||||
height_out * width_out * group * kernel_h * kernel_w * 2;
|
||||
auto per_mask_size = height_out * width_out * group * kernel_h * kernel_w;
|
||||
for (int n = 0; n < batch / im2col_step_; ++n) {
|
||||
auto columns = output_n.select(0, n);
|
||||
// AT_DISPATCH_FLOATING_TYPES(
|
||||
AT_DISPATCH_FLOATING_TYPES_AND_HALF(
|
||||
input.type(), "ms_deform_attn_forward_cuda", ([&] {
|
||||
dcnv3_im2col_cuda(
|
||||
at::cuda::getCurrentCUDAStream(),
|
||||
input.data<scalar_t>() + n * im2col_step_ * per_input_size,
|
||||
offset.data<scalar_t>() +
|
||||
n * im2col_step_ * per_offset_size,
|
||||
mask.data<scalar_t>() + n * im2col_step_ * per_mask_size,
|
||||
columns.data<scalar_t>(), kernel_h, kernel_w, stride_h,
|
||||
stride_w, pad_h, pad_w, dilation_h, dilation_w, group,
|
||||
group_channels, batch_n, height_in, width_in, height_out,
|
||||
width_out, offset_scale);
|
||||
}));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<at::Tensor>
|
||||
dcnv3_cuda_backward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h, const int stride_w,
|
||||
const int pad_h, const int pad_w, const int dilation_h,
|
||||
const int dilation_w, const int group,
|
||||
const int group_channels, const float offset_scale,
|
||||
const at::Tensor &grad_output, const int im2col_step) {
|
||||
|
||||
AT_ASSERTM(input.is_contiguous(), "input tensor has to be contiguous");
|
||||
AT_ASSERTM(offset.is_contiguous(), "offset tensor has to be contiguous");
|
||||
AT_ASSERTM(mask.is_contiguous(), "mask tensor has to be contiguous");
|
||||
AT_ASSERTM(grad_output.is_contiguous(),
|
||||
"grad_output tensor has to be contiguous");
|
||||
AT_ASSERTM(input.type().is_cuda(), "input must be a CUDA tensor");
|
||||
AT_ASSERTM(offset.type().is_cuda(), "offset must be a CUDA tensor");
|
||||
AT_ASSERTM(mask.type().is_cuda(), "mask must be a CUDA tensor");
|
||||
AT_ASSERTM(grad_output.type().is_cuda(),
|
||||
"grad_output must be a CUDA tensor");
|
||||
|
||||
const int batch = input.size(0);
|
||||
const int height_in = input.size(1);
|
||||
const int width_in = input.size(2);
|
||||
const int channels = input.size(3);
|
||||
const int height_out =
|
||||
(height_in + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h +
|
||||
1;
|
||||
const int width_out =
|
||||
(width_in + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w +
|
||||
1;
|
||||
const int im2col_step_ = std::min(batch, im2col_step);
|
||||
|
||||
AT_ASSERTM(batch % im2col_step_ == 0,
|
||||
"batch(%d) must divide im2col_step(%d)", batch, im2col_step_);
|
||||
AT_ASSERTM(
|
||||
channels == (group * group_channels),
|
||||
"Input channels and group times group channels wont match: (%d vs %d).",
|
||||
channels, group * group_channels);
|
||||
|
||||
auto dtype = input.dtype();
|
||||
if (dtype == at::kHalf) {
|
||||
dtype = at::kFloat;
|
||||
}
|
||||
|
||||
auto grad_input = at::zeros_like(input, dtype);
|
||||
auto grad_offset = at::zeros_like(offset, dtype);
|
||||
auto grad_mask = at::zeros_like(mask, dtype);
|
||||
|
||||
const int batch_n = im2col_step_;
|
||||
auto per_input_size = height_in * width_in * group * group_channels;
|
||||
auto per_offset_size =
|
||||
height_out * width_out * group * kernel_h * kernel_w * 2;
|
||||
auto per_mask_size = height_out * width_out * group * kernel_h * kernel_w;
|
||||
auto grad_output_n =
|
||||
grad_output.view({batch / im2col_step_, batch_n, height_out * width_out,
|
||||
group, group_channels});
|
||||
|
||||
for (int n = 0; n < batch / im2col_step_; ++n) {
|
||||
auto grad_output_g = grad_output_n.select(0, n);
|
||||
// AT_DISPATCH_FLOATING_TYPES(
|
||||
AT_DISPATCH_FLOATING_TYPES_AND_HALF(
|
||||
input.type(), "ms_deform_attn_backward_cuda", ([&] {
|
||||
dcnv3_col2im_cuda(
|
||||
at::cuda::getCurrentCUDAStream(),
|
||||
grad_output_g.data<scalar_t>(),
|
||||
input.data<scalar_t>() + n * im2col_step_ * per_input_size,
|
||||
offset.data<scalar_t>() +
|
||||
n * im2col_step_ * per_offset_size,
|
||||
mask.data<scalar_t>() + n * im2col_step_ * per_mask_size,
|
||||
kernel_h, kernel_w, stride_h, stride_w, pad_h, pad_w,
|
||||
dilation_h, dilation_w, group, group_channels, batch_n,
|
||||
height_in, width_in, height_out, width_out, offset_scale,
|
||||
grad_input.data<opmath_t>() +
|
||||
n * im2col_step_ * per_input_size,
|
||||
grad_offset.data<opmath_t>() +
|
||||
n * im2col_step_ * per_offset_size,
|
||||
grad_mask.data<opmath_t>() +
|
||||
n * im2col_step_ * per_mask_size);
|
||||
}));
|
||||
}
|
||||
|
||||
if (input.dtype() == torch::kHalf) {
|
||||
return {grad_input.to(torch::kHalf), grad_offset.to(torch::kHalf),
|
||||
grad_mask.to(torch::kHalf)};
|
||||
} else {
|
||||
return {grad_input, grad_offset, grad_mask};
|
||||
}
|
||||
}
|
||||
31
detection/ops_dcnv3/src/cuda/dcnv3_cuda.h
Normal file
31
detection/ops_dcnv3/src/cuda/dcnv3_cuda.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
**************************************************************************************************
|
||||
* InternImage
|
||||
* Copyright (c) 2022 OpenGVLab
|
||||
* Licensed under The MIT License [see LICENSE for details]
|
||||
**************************************************************************************************
|
||||
* Modified from
|
||||
*https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0
|
||||
**************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <torch/extension.h>
|
||||
|
||||
at::Tensor dcnv3_cuda_forward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h,
|
||||
const int stride_w, const int pad_h,
|
||||
const int pad_w, const int dilation_h,
|
||||
const int dilation_w, const int group,
|
||||
const int group_channels,
|
||||
const float offset_scale, const int im2col_step);
|
||||
|
||||
std::vector<at::Tensor>
|
||||
dcnv3_cuda_backward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h, const int stride_w,
|
||||
const int pad_h, const int pad_w, const int dilation_h,
|
||||
const int dilation_w, const int group,
|
||||
const int group_channels, const float offset_scale,
|
||||
const at::Tensor &grad_output, const int im2col_step);
|
||||
1045
detection/ops_dcnv3/src/cuda/dcnv3_im2col_cuda.cuh
Normal file
1045
detection/ops_dcnv3/src/cuda/dcnv3_im2col_cuda.cuh
Normal file
File diff suppressed because it is too large
Load Diff
59
detection/ops_dcnv3/src/dcnv3.h
Normal file
59
detection/ops_dcnv3/src/dcnv3.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
**************************************************************************************************
|
||||
* InternImage
|
||||
* Copyright (c) 2022 OpenGVLab
|
||||
* Licensed under The MIT License [see LICENSE for details]
|
||||
**************************************************************************************************
|
||||
* Modified from
|
||||
*https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0
|
||||
**************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpu/dcnv3_cpu.h"
|
||||
|
||||
#ifdef WITH_CUDA
|
||||
#include "cuda/dcnv3_cuda.h"
|
||||
#endif
|
||||
|
||||
at::Tensor dcnv3_forward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h,
|
||||
const int kernel_w, const int stride_h,
|
||||
const int stride_w, const int pad_h, const int pad_w,
|
||||
const int dilation_h, const int dilation_w,
|
||||
const int group, const int group_channels,
|
||||
const float offset_scale, const int im2col_step) {
|
||||
if (input.type().is_cuda()) {
|
||||
#ifdef WITH_CUDA
|
||||
return dcnv3_cuda_forward(input, offset, mask, kernel_h, kernel_w,
|
||||
stride_h, stride_w, pad_h, pad_w, dilation_h,
|
||||
dilation_w, group, group_channels,
|
||||
offset_scale, im2col_step);
|
||||
#else
|
||||
AT_ERROR("Not compiled with GPU support");
|
||||
#endif
|
||||
}
|
||||
AT_ERROR("Not implemented on the CPU");
|
||||
}
|
||||
|
||||
std::vector<at::Tensor>
|
||||
dcnv3_backward(const at::Tensor &input, const at::Tensor &offset,
|
||||
const at::Tensor &mask, const int kernel_h, const int kernel_w,
|
||||
const int stride_h, const int stride_w, const int pad_h,
|
||||
const int pad_w, const int dilation_h, const int dilation_w,
|
||||
const int group, const int group_channels,
|
||||
const float offset_scale, const at::Tensor &grad_output,
|
||||
const int im2col_step) {
|
||||
if (input.type().is_cuda()) {
|
||||
#ifdef WITH_CUDA
|
||||
return dcnv3_cuda_backward(input, offset, mask, kernel_h, kernel_w,
|
||||
stride_h, stride_w, pad_h, pad_w, dilation_h,
|
||||
dilation_w, group, group_channels,
|
||||
offset_scale, grad_output, im2col_step);
|
||||
#else
|
||||
AT_ERROR("Not compiled with GPU support");
|
||||
#endif
|
||||
}
|
||||
AT_ERROR("Not implemented on the CPU");
|
||||
}
|
||||
17
detection/ops_dcnv3/src/vision.cpp
Normal file
17
detection/ops_dcnv3/src/vision.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*!
|
||||
**************************************************************************************************
|
||||
* InternImage
|
||||
* Copyright (c) 2022 OpenGVLab
|
||||
* Licensed under The MIT License [see LICENSE for details]
|
||||
**************************************************************************************************
|
||||
* Modified from
|
||||
*https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0
|
||||
**************************************************************************************************
|
||||
*/
|
||||
|
||||
#include "dcnv3.h"
|
||||
|
||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
||||
m.def("dcnv3_forward", &dcnv3_forward, "dcnv3_forward");
|
||||
m.def("dcnv3_backward", &dcnv3_backward, "dcnv3_backward");
|
||||
}
|
||||
263
detection/ops_dcnv3/test.py
Normal file
263
detection/ops_dcnv3/test.py
Normal file
@@ -0,0 +1,263 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import division
|
||||
|
||||
import time
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import math
|
||||
from torch.autograd import gradcheck
|
||||
|
||||
from functions.dcnv3_func import DCNv3Function, dcnv3_core_pytorch
|
||||
|
||||
H_in, W_in = 8, 8
|
||||
N, M, D = 2, 4, 16
|
||||
Kh, Kw = 3, 3
|
||||
P = Kh * Kw
|
||||
offset_scale = 2.0
|
||||
pad = 1
|
||||
dilation = 1
|
||||
stride = 1
|
||||
H_out = (H_in + 2 * pad - (dilation * (Kh - 1) + 1)) // stride + 1
|
||||
W_out = (W_in + 2 * pad - (dilation * (Kw - 1) + 1)) // stride + 1
|
||||
|
||||
torch.manual_seed(3)
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def check_forward_equal_with_pytorch_double():
|
||||
input = torch.rand(N, H_in, W_in, M*D).cuda() * 0.01
|
||||
offset = torch.rand(N, H_out, W_out, M*P*2).cuda() * 10
|
||||
mask = torch.rand(N, H_out, W_out, M, P).cuda() + 1e-5
|
||||
mask /= mask.sum(-1, keepdim=True)
|
||||
mask = mask.reshape(N, H_out, W_out, M*P)
|
||||
|
||||
output_pytorch = dcnv3_core_pytorch(
|
||||
input.double(),
|
||||
offset.double(),
|
||||
mask.double(),
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, offset_scale).detach().cpu()
|
||||
|
||||
im2col_step = 2
|
||||
output_cuda = DCNv3Function.apply(
|
||||
input.double(),
|
||||
offset.double(),
|
||||
mask.double(),
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, offset_scale,
|
||||
im2col_step).detach().cpu()
|
||||
|
||||
fwdok = torch.allclose(output_cuda, output_pytorch)
|
||||
max_abs_err = (output_cuda - output_pytorch).abs().max()
|
||||
max_rel_err = ((output_cuda - output_pytorch).abs() /
|
||||
output_pytorch.abs()).max()
|
||||
print('>>> forward double')
|
||||
print(f'* {fwdok} check_forward_equal_with_pytorch_double: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def check_forward_equal_with_pytorch_float():
|
||||
input = torch.rand(N, H_in, W_in, M*D).cuda() * 0.01
|
||||
offset = torch.rand(N, H_out, W_out, M*P*2).cuda() * 10
|
||||
mask = torch.rand(N, H_out, W_out, M, P).cuda() + 1e-5
|
||||
mask /= mask.sum(-1, keepdim=True)
|
||||
mask = mask.reshape(N, H_out, W_out, M*P)
|
||||
|
||||
output_pytorch = dcnv3_core_pytorch(
|
||||
input,
|
||||
offset,
|
||||
mask,
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, offset_scale).detach().cpu()
|
||||
|
||||
im2col_step = 2
|
||||
output_cuda = DCNv3Function.apply(
|
||||
input,
|
||||
offset,
|
||||
mask,
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, offset_scale,
|
||||
im2col_step).detach().cpu()
|
||||
|
||||
fwdok = torch.allclose(output_cuda, output_pytorch, rtol=1e-2, atol=1e-3)
|
||||
max_abs_err = (output_cuda - output_pytorch).abs().max()
|
||||
max_rel_err = ((output_cuda - output_pytorch).abs() /
|
||||
output_pytorch.abs()).max()
|
||||
print('>>> forward float')
|
||||
print(f'* {fwdok} check_forward_equal_with_pytorch_float: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')
|
||||
|
||||
|
||||
def check_backward_equal_with_pytorch_double(channels=4, grad_input=True, grad_offset=True, grad_mask=True):
|
||||
# H_in, W_in = 4, 4
|
||||
N = 2
|
||||
M = 2
|
||||
H_out = (H_in + 2 * pad - (dilation * (Kh - 1) + 1)) // stride + 1
|
||||
W_out = (W_in + 2 * pad - (dilation * (Kw - 1) + 1)) // stride + 1
|
||||
|
||||
D = channels
|
||||
input0 = torch.rand(N, H_in, W_in, M*D).cuda() * 0.01
|
||||
offset0 = torch.rand(N, H_out, W_out, M*P*2).cuda() * 10
|
||||
mask0 = torch.rand(N, H_out, W_out, M, P).cuda() + 1e-5
|
||||
mask0 /= mask0.sum(-1, keepdim=True)
|
||||
mask0 = mask0.reshape(N, H_out, W_out, M*P)
|
||||
input0.requires_grad = grad_input
|
||||
offset0.requires_grad = grad_offset
|
||||
mask0.requires_grad = grad_mask
|
||||
|
||||
output_pytorch = dcnv3_core_pytorch(
|
||||
input0.double(),
|
||||
offset0.double(),
|
||||
mask0.double(),
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, offset_scale)
|
||||
output_pytorch.sum().backward()
|
||||
|
||||
input1 = input0.detach()
|
||||
offset1 = offset0.detach()
|
||||
mask1 = mask0.detach()
|
||||
input1.requires_grad = grad_input
|
||||
offset1.requires_grad = grad_offset
|
||||
mask1.requires_grad = grad_mask
|
||||
|
||||
im2col_step = 2
|
||||
output_cuda = DCNv3Function.apply(
|
||||
input1.double(),
|
||||
offset1.double(),
|
||||
mask1.double(),
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, offset_scale,
|
||||
im2col_step)
|
||||
output_cuda.sum().backward()
|
||||
|
||||
print(f'>>> backward double: channels {D}')
|
||||
bwdok = torch.allclose(input0.grad, input1.grad, rtol=1e-2, atol=1e-3)
|
||||
max_abs_err = (input0.grad - input1.grad).abs().max()
|
||||
max_rel_err = ((input0.grad - input1.grad).abs() /
|
||||
input0.grad.abs()).max()
|
||||
print(
|
||||
f'* {bwdok} input_grad check_backward_equal_with_pytorch_double: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')
|
||||
|
||||
bwdok = torch.allclose(offset0.grad, offset1.grad, rtol=1e-2, atol=1e-3)
|
||||
max_abs_err = (offset0.grad - offset1.grad).abs().max()
|
||||
max_rel_err = ((offset0.grad - offset1.grad).abs() /
|
||||
offset0.grad.abs()).max()
|
||||
print(
|
||||
f'* {bwdok} offset_grad check_backward_equal_with_pytorch_double: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')
|
||||
|
||||
bwdok = torch.allclose(mask0.grad, mask1.grad, rtol=1e-2, atol=1e-3)
|
||||
max_abs_err = (mask0.grad - mask1.grad).abs().max()
|
||||
max_rel_err = ((mask0.grad - mask1.grad).abs() /
|
||||
mask0.grad.abs()).max()
|
||||
print(
|
||||
f'* {bwdok} mask_grad check_backward_equal_with_pytorch_double: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')
|
||||
|
||||
|
||||
def check_backward_equal_with_pytorch_float(channels=4, grad_input=True, grad_offset=True, grad_mask=True):
|
||||
# H_in, W_in = 4, 4
|
||||
N = 2
|
||||
M = 2
|
||||
H_out = (H_in + 2 * pad - (dilation * (Kh - 1) + 1)) // stride + 1
|
||||
W_out = (W_in + 2 * pad - (dilation * (Kw - 1) + 1)) // stride + 1
|
||||
|
||||
D = channels
|
||||
input0 = torch.rand(N, H_in, W_in, M*D).cuda() * 0.01
|
||||
offset0 = torch.rand(N, H_out, W_out, M*P*2).cuda() * 10
|
||||
mask0 = torch.rand(N, H_out, W_out, M, P).cuda() + 1e-5
|
||||
mask0 /= mask0.sum(-1, keepdim=True)
|
||||
mask0 = mask0.reshape(N, H_out, W_out, M*P)
|
||||
input0.requires_grad = grad_input
|
||||
offset0.requires_grad = grad_offset
|
||||
mask0.requires_grad = grad_mask
|
||||
|
||||
output_pytorch = dcnv3_core_pytorch(
|
||||
input0,
|
||||
offset0,
|
||||
mask0,
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, offset_scale)
|
||||
output_pytorch.sum().backward()
|
||||
|
||||
input1 = input0.detach()
|
||||
offset1 = offset0.detach()
|
||||
mask1 = mask0.detach()
|
||||
input1.requires_grad = grad_input
|
||||
offset1.requires_grad = grad_offset
|
||||
mask1.requires_grad = grad_mask
|
||||
|
||||
im2col_step = 2
|
||||
output_cuda = DCNv3Function.apply(
|
||||
input1,
|
||||
offset1,
|
||||
mask1,
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, offset_scale,
|
||||
im2col_step)
|
||||
output_cuda.sum().backward()
|
||||
|
||||
print(f'>>> backward float: channels {D}')
|
||||
bwdok = torch.allclose(input0.grad, input1.grad, rtol=1e-2, atol=1e-3)
|
||||
max_abs_err = (input0.grad - input1.grad).abs().max()
|
||||
max_rel_err = ((input0.grad - input1.grad).abs() /
|
||||
input0.grad.abs()).max()
|
||||
print(
|
||||
f'* {bwdok} input_grad check_backward_equal_with_pytorch_float: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')
|
||||
|
||||
bwdok = torch.allclose(offset0.grad, offset1.grad, rtol=1e-2, atol=1e-3)
|
||||
max_abs_err = (offset0.grad - offset1.grad).abs().max()
|
||||
max_rel_err = ((offset0.grad - offset1.grad).abs() /
|
||||
offset0.grad.abs()).max()
|
||||
print(
|
||||
f'* {bwdok} offset_grad check_backward_equal_with_pytorch_float: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')
|
||||
|
||||
bwdok = torch.allclose(mask0.grad, mask1.grad, rtol=1e-2, atol=1e-3)
|
||||
max_abs_err = (mask0.grad - mask1.grad).abs().max()
|
||||
max_rel_err = ((mask0.grad - mask1.grad).abs() /
|
||||
mask0.grad.abs()).max()
|
||||
print(
|
||||
f'* {bwdok} mask_grad check_backward_equal_with_pytorch_float: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def check_time_cost(im2col_step=128):
|
||||
N = 512
|
||||
H_in, W_in = 64, 64
|
||||
H_out = (H_in + 2 * pad - (dilation * (Kh - 1) + 1)) // stride + 1
|
||||
W_out = (W_in + 2 * pad - (dilation * (Kw - 1) + 1)) // stride + 1
|
||||
|
||||
input = torch.rand(N, H_in, W_in, M*D).cuda() * 0.01
|
||||
offset = torch.rand(N, H_out, W_out, M*P*2).cuda() * 10
|
||||
mask = torch.rand(N, H_out, W_out, M, P).cuda() + 1e-5
|
||||
mask /= mask.sum(-1, keepdim=True)
|
||||
mask = mask.reshape(N, H_out, W_out, M*P)
|
||||
print(
|
||||
f'>>> time cost: im2col_step {im2col_step}; input {input.shape}; points {P} ')
|
||||
repeat = 100
|
||||
for i in range(repeat):
|
||||
output_cuda = DCNv3Function.apply(
|
||||
input,
|
||||
offset,
|
||||
mask,
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, 1.0,
|
||||
im2col_step)
|
||||
torch.cuda.synchronize()
|
||||
start = time.time()
|
||||
for i in range(repeat):
|
||||
output_cuda = DCNv3Function.apply(
|
||||
input,
|
||||
offset,
|
||||
mask,
|
||||
Kh, Kw, stride, stride, Kh // 2, Kw // 2, dilation, dilation, M, D, 1.0,
|
||||
im2col_step)
|
||||
torch.cuda.synchronize()
|
||||
print(f'foward time cost: {(time.time() - start) / repeat}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
check_forward_equal_with_pytorch_double()
|
||||
check_forward_equal_with_pytorch_float()
|
||||
for channels in [1, 16, 30, 32, 64, 71, 1025]:
|
||||
check_backward_equal_with_pytorch_double(channels, True, True, True)
|
||||
for channels in [1, 16, 30, 32, 64, 71, 1025]:
|
||||
check_backward_equal_with_pytorch_float(channels, True, True, True)
|
||||
for i in range(3):
|
||||
im2col_step = 128 * (2 ** i)
|
||||
check_time_cost(im2col_step)
|
||||
25
detection/slurm_test.sh
Normal file
25
detection/slurm_test.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
PARTITION=$1
|
||||
JOB_NAME=$2
|
||||
CONFIG=$3
|
||||
CHECKPOINT=$4
|
||||
GPUS=${GPUS:-8}
|
||||
GPUS_PER_NODE=${GPUS_PER_NODE:-8}
|
||||
CPUS_PER_TASK=${CPUS_PER_TASK:-5}
|
||||
PY_ARGS=${@:5}
|
||||
SRUN_ARGS=${SRUN_ARGS:-""}
|
||||
|
||||
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
|
||||
srun -p ${PARTITION} \
|
||||
--job-name=${JOB_NAME} \
|
||||
--gres=gpu:${GPUS_PER_NODE} \
|
||||
--ntasks=${GPUS} \
|
||||
--ntasks-per-node=${GPUS_PER_NODE} \
|
||||
--cpus-per-task=${CPUS_PER_TASK} \
|
||||
--kill-on-bad-exit=1 \
|
||||
--quotatype=spot \
|
||||
${SRUN_ARGS} \
|
||||
python -u test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS}
|
||||
25
detection/slurm_train.sh
Executable file
25
detection/slurm_train.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
PARTITION=$1
|
||||
JOB_NAME=$2
|
||||
CONFIG=$3
|
||||
WORK_DIR=$4
|
||||
GPUS=${GPUS:-8}
|
||||
GPUS_PER_NODE=${GPUS_PER_NODE:-8}
|
||||
CPUS_PER_TASK=${CPUS_PER_TASK:-10}
|
||||
SRUN_ARGS=${SRUN_ARGS:-""}
|
||||
PY_ARGS=${@:5}
|
||||
|
||||
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
|
||||
srun -p ${PARTITION} \
|
||||
--job-name=${JOB_NAME} \
|
||||
--gres=gpu:${GPUS_PER_NODE} \
|
||||
--ntasks=${GPUS} \
|
||||
--ntasks-per-node=${GPUS_PER_NODE} \
|
||||
--cpus-per-task=${CPUS_PER_TASK} \
|
||||
--kill-on-bad-exit=1 \
|
||||
--quotatype=reserved \
|
||||
${SRUN_ARGS} \
|
||||
python -u train.py ${CONFIG} --work-dir=${WORK_DIR} --launcher="slurm" ${PY_ARGS}
|
||||
265
detection/test.py
Normal file
265
detection/test.py
Normal file
@@ -0,0 +1,265 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path as osp
|
||||
import time
|
||||
import warnings
|
||||
|
||||
import mmcv
|
||||
import torch
|
||||
from mmcv import Config, DictAction
|
||||
from mmcv.cnn import fuse_conv_bn
|
||||
from mmcv.parallel import MMDataParallel, MMDistributedDataParallel
|
||||
from mmcv.runner import (get_dist_info, init_dist, load_checkpoint,
|
||||
wrap_fp16_model)
|
||||
from mmdet.apis import multi_gpu_test, single_gpu_test
|
||||
from mmdet.datasets import (build_dataloader, build_dataset,
|
||||
replace_ImageToTensor)
|
||||
from mmdet.models import build_detector
|
||||
import mmdet_custom # noqa: F401,F403
|
||||
import mmcv_custom # noqa: F401,F403
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='MMDet test (and eval) a model')
|
||||
parser.add_argument('config', help='test config file path')
|
||||
parser.add_argument('checkpoint', help='checkpoint file')
|
||||
parser.add_argument(
|
||||
'--work-dir',
|
||||
help='the directory to save the file containing evaluation metrics')
|
||||
parser.add_argument('--out', help='output result file in pickle format')
|
||||
parser.add_argument(
|
||||
'--fuse-conv-bn',
|
||||
action='store_true',
|
||||
help='Whether to fuse conv and bn, this will slightly increase'
|
||||
'the inference speed')
|
||||
parser.add_argument('--gpu-ids',
|
||||
type=int,
|
||||
nargs='+',
|
||||
help='ids of gpus to use '
|
||||
'(only applicable to non-distributed testing)')
|
||||
parser.add_argument(
|
||||
'--format-only',
|
||||
action='store_true',
|
||||
help='Format the output results without perform evaluation. It is'
|
||||
'useful when you want to format the result to a specific format and '
|
||||
'submit it to the test server')
|
||||
parser.add_argument(
|
||||
'--eval',
|
||||
type=str,
|
||||
nargs='+',
|
||||
help='evaluation metrics, which depends on the dataset, e.g., "bbox",'
|
||||
' "segm", "proposal" for COCO, and "mAP", "recall" for PASCAL VOC')
|
||||
parser.add_argument('--show', action='store_true', help='show results')
|
||||
parser.add_argument('--show-dir',
|
||||
help='directory where painted images will be saved')
|
||||
parser.add_argument('--show-score-thr',
|
||||
type=float,
|
||||
default=0.3,
|
||||
help='score threshold (default: 0.3)')
|
||||
parser.add_argument('--gpu-collect',
|
||||
action='store_true',
|
||||
help='whether to use gpu to collect results.')
|
||||
parser.add_argument(
|
||||
'--tmpdir',
|
||||
help='tmp directory used for collecting results from multiple '
|
||||
'workers, available when gpu-collect is not specified')
|
||||
parser.add_argument(
|
||||
'--cfg-options',
|
||||
nargs='+',
|
||||
action=DictAction,
|
||||
help='override some settings in the used config, the key-value pair '
|
||||
'in xxx=yyy format will be merged into config file. If the value to '
|
||||
'be overwritten is a list, it should be like key="[a,b]" or key=a,b '
|
||||
'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" '
|
||||
'Note that the quotation marks are necessary and that no white space '
|
||||
'is allowed.')
|
||||
parser.add_argument(
|
||||
'--options',
|
||||
nargs='+',
|
||||
action=DictAction,
|
||||
help='custom options for evaluation, the key-value pair in xxx=yyy '
|
||||
'format will be kwargs for dataset.evaluate() function (deprecate), '
|
||||
'change to --eval-options instead.')
|
||||
parser.add_argument(
|
||||
'--eval-options',
|
||||
nargs='+',
|
||||
action=DictAction,
|
||||
help='custom options for evaluation, the key-value pair in xxx=yyy '
|
||||
'format will be kwargs for dataset.evaluate() function')
|
||||
parser.add_argument('--launcher',
|
||||
choices=['none', 'pytorch', 'slurm', 'mpi'],
|
||||
default='none',
|
||||
help='job launcher')
|
||||
parser.add_argument('--local_rank', type=int, default=0)
|
||||
args = parser.parse_args()
|
||||
if 'LOCAL_RANK' not in os.environ:
|
||||
os.environ['LOCAL_RANK'] = str(args.local_rank)
|
||||
|
||||
if args.options and args.eval_options:
|
||||
raise ValueError(
|
||||
'--options and --eval-options cannot be both '
|
||||
'specified, --options is deprecated in favor of --eval-options')
|
||||
if args.options:
|
||||
warnings.warn('--options is deprecated in favor of --eval-options')
|
||||
args.eval_options = args.options
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
args = parse_args()
|
||||
|
||||
assert args.out or args.eval or args.format_only or args.show \
|
||||
or args.show_dir, \
|
||||
('Please specify at least one operation (save/eval/format/show the '
|
||||
'results / save the results) with the argument "--out", "--eval"'
|
||||
', "--format-only", "--show" or "--show-dir"')
|
||||
|
||||
if args.eval and args.format_only:
|
||||
raise ValueError('--eval and --format_only cannot be both specified')
|
||||
|
||||
if args.out is not None and not args.out.endswith(('.pkl', '.pickle')):
|
||||
raise ValueError('The output file must be a pkl file.')
|
||||
|
||||
cfg = Config.fromfile(args.config)
|
||||
if args.cfg_options is not None:
|
||||
cfg.merge_from_dict(args.cfg_options)
|
||||
# set cudnn_benchmark
|
||||
if cfg.get('cudnn_benchmark', False):
|
||||
torch.backends.cudnn.benchmark = True
|
||||
|
||||
cfg.model.pretrained = None
|
||||
if cfg.model.get('neck'):
|
||||
if isinstance(cfg.model.neck, list):
|
||||
for neck_cfg in cfg.model.neck:
|
||||
if neck_cfg.get('rfp_backbone'):
|
||||
if neck_cfg.rfp_backbone.get('pretrained'):
|
||||
neck_cfg.rfp_backbone.pretrained = None
|
||||
elif cfg.model.neck.get('rfp_backbone'):
|
||||
if cfg.model.neck.rfp_backbone.get('pretrained'):
|
||||
cfg.model.neck.rfp_backbone.pretrained = None
|
||||
|
||||
# in case the test dataset is concatenated
|
||||
samples_per_gpu = 1
|
||||
if isinstance(cfg.data.test, dict):
|
||||
cfg.data.test.test_mode = True
|
||||
samples_per_gpu = cfg.data.test.pop('samples_per_gpu', 1)
|
||||
if samples_per_gpu > 1:
|
||||
# Replace 'ImageToTensor' to 'DefaultFormatBundle'
|
||||
cfg.data.test.pipeline = replace_ImageToTensor(
|
||||
cfg.data.test.pipeline)
|
||||
elif isinstance(cfg.data.test, list):
|
||||
for ds_cfg in cfg.data.test:
|
||||
ds_cfg.test_mode = True
|
||||
samples_per_gpu = max(
|
||||
[ds_cfg.pop('samples_per_gpu', 1) for ds_cfg in cfg.data.test])
|
||||
if samples_per_gpu > 1:
|
||||
for ds_cfg in cfg.data.test:
|
||||
ds_cfg.pipeline = replace_ImageToTensor(ds_cfg.pipeline)
|
||||
|
||||
if args.gpu_ids is not None:
|
||||
cfg.gpu_ids = args.gpu_ids
|
||||
else:
|
||||
cfg.gpu_ids = range(1)
|
||||
|
||||
|
||||
# init distributed env first, since logger depends on the dist info.
|
||||
if args.launcher == 'none':
|
||||
distributed = False
|
||||
if len(cfg.gpu_ids) > 1:
|
||||
warnings.warn(
|
||||
f'We treat {cfg.gpu_ids} as gpu-ids, and reset to '
|
||||
f'{cfg.gpu_ids[0:1]} as gpu-ids to avoid potential error in '
|
||||
'non-distribute testing time.')
|
||||
cfg.gpu_ids = cfg.gpu_ids[0:1]
|
||||
else:
|
||||
distributed = True
|
||||
init_dist(args.launcher, **cfg.dist_params)
|
||||
|
||||
|
||||
rank, _ = get_dist_info()
|
||||
# allows not to create
|
||||
if args.work_dir is not None and rank == 0:
|
||||
mmcv.mkdir_or_exist(osp.abspath(args.work_dir))
|
||||
timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime())
|
||||
json_file = osp.join(args.work_dir, f'eval_{timestamp}.json')
|
||||
|
||||
# build the dataloader
|
||||
dataset = build_dataset(cfg.data.test)
|
||||
data_loader = build_dataloader(dataset,
|
||||
samples_per_gpu=samples_per_gpu,
|
||||
workers_per_gpu=cfg.data.workers_per_gpu,
|
||||
dist=distributed,
|
||||
shuffle=False)
|
||||
|
||||
# build the model and load checkpoint
|
||||
cfg.model.train_cfg = None
|
||||
model = build_detector(cfg.model, test_cfg=cfg.get('test_cfg'))
|
||||
print(model)
|
||||
model_without_ddp = model
|
||||
n_parameters = sum(p.numel() for p in model.parameters()
|
||||
if p.requires_grad)
|
||||
print(f"number of params: {n_parameters}")
|
||||
|
||||
|
||||
fp16_cfg = cfg.get('fp16', None)
|
||||
if fp16_cfg is not None:
|
||||
wrap_fp16_model(model)
|
||||
checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu')
|
||||
|
||||
if args.fuse_conv_bn:
|
||||
model = fuse_conv_bn(model)
|
||||
# old versions did not save class info in checkpoints, this walkaround is
|
||||
# for backward compatibility
|
||||
if 'CLASSES' in checkpoint.get('meta', {}):
|
||||
model.CLASSES = checkpoint['meta']['CLASSES']
|
||||
else:
|
||||
model.CLASSES = dataset.CLASSES
|
||||
|
||||
if not distributed:
|
||||
model = MMDataParallel(model, device_ids=cfg.gpu_ids)
|
||||
outputs = single_gpu_test(model, data_loader, args.show, args.show_dir,
|
||||
args.show_score_thr)
|
||||
else:
|
||||
model = MMDistributedDataParallel(
|
||||
model.cuda(),
|
||||
device_ids=[torch.cuda.current_device()],
|
||||
broadcast_buffers=False)
|
||||
outputs = multi_gpu_test(model, data_loader, args.tmpdir,
|
||||
args.gpu_collect)
|
||||
|
||||
|
||||
rank, _ = get_dist_info()
|
||||
if rank == 0:
|
||||
if args.out:
|
||||
print(f'\nwriting results to {args.out}')
|
||||
mmcv.dump(outputs, args.out)
|
||||
kwargs = {} if args.eval_options is None else args.eval_options
|
||||
if args.format_only:
|
||||
dataset.format_results(outputs, **kwargs)
|
||||
if args.eval:
|
||||
eval_kwargs = cfg.get('evaluation', {}).copy()
|
||||
# hard-code way to remove EvalHook args
|
||||
for key in [
|
||||
'interval', 'tmpdir', 'start', 'gpu_collect', 'save_best',
|
||||
'rule', 'dynamic_intervals'
|
||||
]:
|
||||
eval_kwargs.pop(key, None)
|
||||
eval_kwargs.update(dict(metric=args.eval, **kwargs))
|
||||
metric = dataset.evaluate(outputs, **eval_kwargs)
|
||||
print(metric)
|
||||
metric_dict = dict(config=args.config, metric=metric)
|
||||
if args.work_dir is not None and rank == 0:
|
||||
mmcv.dump(metric_dict, json_file)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
95
detection/tools/create_crowd_anno.py
Normal file
95
detection/tools/create_crowd_anno.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import argparse
|
||||
import os
|
||||
import pickle as pkl
|
||||
import numpy as np
|
||||
import random
|
||||
from PIL import Image
|
||||
import concurrent.futures
|
||||
import json
|
||||
import mmcv
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Generate MMDetection Annotations for Crowdhuman-like dataset')
|
||||
parser.add_argument('--dataset', help='dataset name', type=str)
|
||||
parser.add_argument('--dataset-split', help='dataset split, e.g. train, val', type=str)
|
||||
|
||||
args = parser.parse_args()
|
||||
return args.dataset, args.dataset_split
|
||||
|
||||
def load_func(fpath):
|
||||
assert os.path.exists(fpath)
|
||||
with open(fpath, 'r') as fid:
|
||||
lines = fid.readlines()
|
||||
records = [json.loads(line.strip('\n')) for line in lines]
|
||||
return records
|
||||
|
||||
def decode_annotations(records, dataset_path):
|
||||
rec_ids = list(range(len(records)))
|
||||
img_list = []
|
||||
ann_list = []
|
||||
ann_id = 1
|
||||
for idx, rec_id in enumerate(rec_ids):
|
||||
img_id = records[rec_id]['ID']
|
||||
img_url = dataset_path + 'Images/' + img_id + '.jpg'
|
||||
assert os.path.exists(img_url)
|
||||
im = Image.open(img_url)
|
||||
im_w, im_h = im.width, im.height
|
||||
|
||||
gt_box = records[rec_id]['gtboxes']
|
||||
gt_box_len = len(gt_box)
|
||||
img_dict = dict(
|
||||
file_name=img_id + '.jpg',
|
||||
height=im_h,
|
||||
width=im_w,
|
||||
id=idx
|
||||
)
|
||||
img_list.append(img_dict)
|
||||
for ii in range(gt_box_len):
|
||||
each_data = gt_box[ii]
|
||||
x, y, w, h = each_data['fbox']
|
||||
|
||||
if w <= 0 or h <= 0:
|
||||
continue
|
||||
# x1 = x; y1 = y; x2 = x + w; y2 = y + h
|
||||
|
||||
valid_bbox = [x, y, w, h]
|
||||
if each_data['tag'] == 'person':
|
||||
tag = 1
|
||||
else:
|
||||
tag = -2
|
||||
if 'extra' in each_data:
|
||||
if 'ignore' in each_data['extra']:
|
||||
if each_data['extra']['ignore'] != 0:
|
||||
tag = -2
|
||||
ann_dict = dict(
|
||||
area=w * h,
|
||||
iscrowd=1 if tag == -2 else 0,
|
||||
image_id=idx,
|
||||
bbox=[x, y, w, h],
|
||||
category_id=1,
|
||||
id=ann_id,
|
||||
# ignore=1 if tag == -2 else 1,
|
||||
)
|
||||
ann_id += 1
|
||||
ann_list.append(ann_dict)
|
||||
cate_list = [{'supercategory': 'none', 'id': 1, 'name': 'person'}]
|
||||
json_dict = dict(
|
||||
images=img_list,
|
||||
annotations=ann_list,
|
||||
categories=cate_list
|
||||
)
|
||||
return json_dict
|
||||
|
||||
if __name__ == "__main__":
|
||||
dataset_name, dataset_type = parse_args()
|
||||
dataset_path = 'data/%s/' % dataset_name
|
||||
ch_file_path = dataset_path + 'annotations/annotation_%s.odgt' % dataset_type
|
||||
json_file_path = dataset_path + 'annotations/annotation_%s.json' % dataset_type
|
||||
|
||||
records = load_func(ch_file_path)
|
||||
print("Loading Annotations Done")
|
||||
|
||||
json_dict = decode_annotations(records, dataset_path)
|
||||
|
||||
print("Parsing Bbox Number: %d" % len(json_dict['annotations']))
|
||||
mmcv.dump(json_dict, json_file_path)
|
||||
2
detection/tools/evaluate/__init__.py
Normal file
2
detection/tools/evaluate/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .compute_APMR import compute_APMR
|
||||
from .compute_JI import compute_JI_with_ignore
|
||||
249
detection/train.py
Normal file
249
detection/train.py
Normal file
@@ -0,0 +1,249 @@
|
||||
# --------------------------------------------------------
|
||||
# DCNv4
|
||||
# Copyright (c) 2024 OpenGVLab
|
||||
# Licensed under The MIT License [see LICENSE for details]
|
||||
# --------------------------------------------------------
|
||||
|
||||
|
||||
import argparse
|
||||
import copy
|
||||
import os
|
||||
import os.path as osp
|
||||
import time
|
||||
import warnings
|
||||
|
||||
import mmcv
|
||||
import torch
|
||||
import torch.distributed as dist
|
||||
from mmcv import Config, DictAction
|
||||
from mmcv.runner import get_dist_info, init_dist
|
||||
from mmcv.utils import get_git_hash
|
||||
|
||||
from mmdet import __version__
|
||||
from mmdet.apis import init_random_seed, set_random_seed, train_detector
|
||||
from mmdet.datasets import build_dataset
|
||||
from mmdet.models import build_detector
|
||||
from mmdet.utils import (collect_env, get_device, get_root_logger,
|
||||
replace_cfg_vals, setup_multi_processes,
|
||||
update_data_root)
|
||||
import mmcv_custom # noqa: F401,F403
|
||||
import mmdet_custom # noqa: F401,F403
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Train a detector')
|
||||
parser.add_argument('config', help='train config file path')
|
||||
parser.add_argument('--work-dir', help='the dir to save logs and models')
|
||||
parser.add_argument('--resume-from',
|
||||
help='the checkpoint file to resume from')
|
||||
parser.add_argument('--auto-resume',
|
||||
action='store_true',
|
||||
help='resume from the latest checkpoint automatically')
|
||||
parser.add_argument(
|
||||
'--no-validate',
|
||||
action='store_true',
|
||||
help='whether not to evaluate the checkpoint during training')
|
||||
group_gpus = parser.add_mutually_exclusive_group()
|
||||
group_gpus.add_argument(
|
||||
'--gpus',
|
||||
type=int,
|
||||
help='(Deprecated, please use --gpu-id) number of gpus to use '
|
||||
'(only applicable to non-distributed training)')
|
||||
group_gpus.add_argument(
|
||||
'--gpu-ids',
|
||||
type=int,
|
||||
nargs='+',
|
||||
help='(Deprecated, please use --gpu-id) ids of gpus to use '
|
||||
'(only applicable to non-distributed training)')
|
||||
group_gpus.add_argument('--gpu-id',
|
||||
type=int,
|
||||
default=0,
|
||||
help='id of gpu to use '
|
||||
'(only applicable to non-distributed training)')
|
||||
parser.add_argument('--seed', type=int, default=None, help='random seed')
|
||||
parser.add_argument(
|
||||
'--diff-seed',
|
||||
action='store_true',
|
||||
help='Whether or not set different seeds for different ranks')
|
||||
parser.add_argument(
|
||||
'--deterministic',
|
||||
action='store_true',
|
||||
help='whether to set deterministic options for CUDNN backend.')
|
||||
parser.add_argument(
|
||||
'--options',
|
||||
nargs='+',
|
||||
action=DictAction,
|
||||
help='override some settings in the used config, the key-value pair '
|
||||
'in xxx=yyy format will be merged into config file (deprecate), '
|
||||
'change to --cfg-options instead.')
|
||||
parser.add_argument(
|
||||
'--cfg-options',
|
||||
nargs='+',
|
||||
action=DictAction,
|
||||
help='override some settings in the used config, the key-value pair '
|
||||
'in xxx=yyy format will be merged into config file. If the value to '
|
||||
'be overwritten is a list, it should be like key="[a,b]" or key=a,b '
|
||||
'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" '
|
||||
'Note that the quotation marks are necessary and that no white space '
|
||||
'is allowed.')
|
||||
parser.add_argument('--launcher',
|
||||
choices=['none', 'pytorch', 'slurm', 'mpi'],
|
||||
default='none',
|
||||
help='job launcher')
|
||||
parser.add_argument('--local_rank', type=int, default=0)
|
||||
parser.add_argument('--auto-scale-lr',
|
||||
action='store_true',
|
||||
help='enable automatically scaling LR.')
|
||||
args = parser.parse_args()
|
||||
if 'LOCAL_RANK' not in os.environ:
|
||||
os.environ['LOCAL_RANK'] = str(args.local_rank)
|
||||
|
||||
if args.options and args.cfg_options:
|
||||
raise ValueError(
|
||||
'--options and --cfg-options cannot be both '
|
||||
'specified, --options is deprecated in favor of --cfg-options')
|
||||
if args.options:
|
||||
warnings.warn('--options is deprecated in favor of --cfg-options')
|
||||
args.cfg_options = args.options
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
|
||||
cfg = Config.fromfile(args.config)
|
||||
|
||||
# replace the ${key} with the value of cfg.key
|
||||
cfg = replace_cfg_vals(cfg)
|
||||
|
||||
# update data root according to MMDET_DATASETS
|
||||
update_data_root(cfg)
|
||||
|
||||
if args.cfg_options is not None:
|
||||
cfg.merge_from_dict(args.cfg_options)
|
||||
|
||||
if args.auto_scale_lr:
|
||||
if 'auto_scale_lr' in cfg and \
|
||||
'enable' in cfg.auto_scale_lr and \
|
||||
'base_batch_size' in cfg.auto_scale_lr:
|
||||
cfg.auto_scale_lr.enable = True
|
||||
else:
|
||||
warnings.warn('Can not find "auto_scale_lr" or '
|
||||
'"auto_scale_lr.enable" or '
|
||||
'"auto_scale_lr.base_batch_size" in your'
|
||||
' configuration file. Please update all the '
|
||||
'configuration files to mmdet >= 2.24.1.')
|
||||
|
||||
# set multi-process settings
|
||||
setup_multi_processes(cfg)
|
||||
|
||||
# set cudnn_benchmark
|
||||
if cfg.get('cudnn_benchmark', False):
|
||||
torch.backends.cudnn.benchmark = True
|
||||
|
||||
# work_dir is determined in this priority: CLI > segment in file > filename
|
||||
if args.work_dir is not None:
|
||||
# update configs according to CLI args if args.work_dir is not None
|
||||
cfg.work_dir = args.work_dir
|
||||
elif cfg.get('work_dir', None) is None:
|
||||
# use config filename as default work_dir if cfg.work_dir is None
|
||||
cfg.work_dir = osp.join('./work_dirs',
|
||||
osp.splitext(osp.basename(args.config))[0])
|
||||
|
||||
if args.resume_from is not None:
|
||||
cfg.resume_from = args.resume_from
|
||||
cfg.auto_resume = args.auto_resume
|
||||
if args.gpus is not None:
|
||||
cfg.gpu_ids = range(1)
|
||||
warnings.warn('`--gpus` is deprecated because we only support '
|
||||
'single GPU mode in non-distributed training. '
|
||||
'Use `gpus=1` now.')
|
||||
if args.gpu_ids is not None:
|
||||
cfg.gpu_ids = args.gpu_ids[0:1]
|
||||
warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. '
|
||||
'Because we only support single GPU mode in '
|
||||
'non-distributed training. Use the first GPU '
|
||||
'in `gpu_ids` now.')
|
||||
if args.gpus is None and args.gpu_ids is None:
|
||||
cfg.gpu_ids = [args.gpu_id]
|
||||
|
||||
# init distributed env first, since logger depends on the dist info.
|
||||
if args.launcher == 'none':
|
||||
distributed = False
|
||||
else:
|
||||
distributed = True
|
||||
init_dist(args.launcher, **cfg.dist_params)
|
||||
# re-set gpu_ids with distributed training mode
|
||||
_, world_size = get_dist_info()
|
||||
cfg.gpu_ids = range(world_size)
|
||||
|
||||
# create work_dir
|
||||
mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir))
|
||||
# dump config
|
||||
cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config)))
|
||||
# init the logger before other steps
|
||||
timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime())
|
||||
log_file = osp.join(cfg.work_dir, f'{timestamp}.log')
|
||||
logger = get_root_logger(log_file=log_file, log_level=cfg.log_level)
|
||||
|
||||
# init the meta dict to record some important information such as
|
||||
# environment info and seed, which will be logged
|
||||
meta = dict()
|
||||
# log env info
|
||||
env_info_dict = collect_env()
|
||||
env_info = '\n'.join([(f'{k}: {v}') for k, v in env_info_dict.items()])
|
||||
dash_line = '-' * 60 + '\n'
|
||||
logger.info('Environment info:\n' + dash_line + env_info + '\n' +
|
||||
dash_line)
|
||||
meta['env_info'] = env_info
|
||||
meta['config'] = cfg.pretty_text
|
||||
# log some basic info
|
||||
logger.info(f'Distributed training: {distributed}')
|
||||
logger.info(f'Config:\n{cfg.pretty_text}')
|
||||
|
||||
cfg.device = get_device()
|
||||
# set random seeds
|
||||
seed = init_random_seed(args.seed, device=cfg.device)
|
||||
seed = seed + dist.get_rank() if args.diff_seed else seed
|
||||
logger.info(f'Set random seed to {seed}, '
|
||||
f'deterministic: {args.deterministic}')
|
||||
set_random_seed(seed, deterministic=args.deterministic)
|
||||
cfg.seed = seed
|
||||
meta['seed'] = seed
|
||||
meta['exp_name'] = osp.basename(args.config)
|
||||
|
||||
model = build_detector(cfg.model,
|
||||
train_cfg=cfg.get('train_cfg'),
|
||||
test_cfg=cfg.get('test_cfg'))
|
||||
model.init_weights()
|
||||
logger.info(model)
|
||||
|
||||
datasets = [build_dataset(cfg.data.train)]
|
||||
if len(cfg.workflow) == 2:
|
||||
val_dataset = copy.deepcopy(cfg.data.val)
|
||||
val_dataset.pipeline = cfg.data.train.pipeline
|
||||
datasets.append(build_dataset(val_dataset))
|
||||
if cfg.checkpoint_config is not None:
|
||||
# save mmdet version, config file content and class names in
|
||||
# checkpoints as meta data
|
||||
cfg.checkpoint_config.meta = dict(mmdet_version=__version__ +
|
||||
get_git_hash()[:7],
|
||||
CLASSES=datasets[0].CLASSES)
|
||||
# add an attribute for visualization convenience
|
||||
model.CLASSES = datasets[0].CLASSES
|
||||
|
||||
torch.backends.cuda.matmul.allow_tf32 = True
|
||||
torch.backends.cudnn.allow_tf32 = True
|
||||
|
||||
train_detector(model,
|
||||
datasets,
|
||||
cfg,
|
||||
distributed=distributed,
|
||||
validate=(not args.no_validate),
|
||||
timestamp=timestamp,
|
||||
meta=meta)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user