add suppoort for det
parent
9abb09c24d
commit
d1894d07f0
|
@ -0,0 +1,121 @@
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
|
||||||
|
data/
|
||||||
|
data
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# custom
|
||||||
|
*.pkl
|
||||||
|
*.pkl.json
|
||||||
|
*.log.json
|
||||||
|
work_dirs/
|
||||||
|
|
||||||
|
# Pytorch
|
||||||
|
*.pth
|
||||||
|
*.py~
|
||||||
|
*.sh~
|
|
@ -0,0 +1,40 @@
|
||||||
|
repos:
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8.git
|
||||||
|
rev: 3.8.3
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
- repo: https://github.com/asottile/seed-isort-config
|
||||||
|
rev: v2.2.0
|
||||||
|
hooks:
|
||||||
|
- id: seed-isort-config
|
||||||
|
- repo: https://github.com/timothycrosley/isort
|
||||||
|
rev: 4.3.21
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-yapf
|
||||||
|
rev: v0.30.0
|
||||||
|
hooks:
|
||||||
|
- id: yapf
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.1.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: check-yaml
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: requirements-txt-fixer
|
||||||
|
- id: double-quote-string-fixer
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: fix-encoding-pragma
|
||||||
|
args: ["--remove"]
|
||||||
|
- id: mixed-line-ending
|
||||||
|
args: ["--fix=lf"]
|
||||||
|
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
||||||
|
rev: 2.1.4
|
||||||
|
hooks:
|
||||||
|
- id: markdownlint
|
||||||
|
args: ["-r", "~MD002,~MD013,~MD024,~MD029,~MD033,~MD034,~MD036", "-t", "allow_different_nesting"]
|
||||||
|
- repo: https://github.com/myint/docformatter
|
||||||
|
rev: v1.3.1
|
||||||
|
hooks:
|
||||||
|
- id: docformatter
|
||||||
|
args: ["--in-place", "--wrap-descriptions", "79"]
|
|
@ -0,0 +1,28 @@
|
||||||
|
import mmcv
|
||||||
|
|
||||||
|
from .version import __version__, short_version
|
||||||
|
|
||||||
|
|
||||||
|
def digit_version(version_str):
|
||||||
|
digit_version = []
|
||||||
|
for x in version_str.split('.'):
|
||||||
|
if x.isdigit():
|
||||||
|
digit_version.append(int(x))
|
||||||
|
elif x.find('rc') != -1:
|
||||||
|
patch_version = x.split('rc')
|
||||||
|
digit_version.append(int(patch_version[0]) - 1)
|
||||||
|
digit_version.append(int(patch_version[1]))
|
||||||
|
return digit_version
|
||||||
|
|
||||||
|
|
||||||
|
mmcv_minimum_version = '1.3.2'
|
||||||
|
mmcv_maximum_version = '1.4.0'
|
||||||
|
mmcv_version = digit_version(mmcv.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
assert (mmcv_version >= digit_version(mmcv_minimum_version)
|
||||||
|
and mmcv_version <= digit_version(mmcv_maximum_version)), \
|
||||||
|
f'MMCV=={mmcv.__version__} is used but incompatible. ' \
|
||||||
|
f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.'
|
||||||
|
|
||||||
|
__all__ = ['__version__', 'short_version']
|
|
@ -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')
|
|
@ -0,0 +1,55 @@
|
||||||
|
# dataset settings
|
||||||
|
dataset_type = 'VOCDataset'
|
||||||
|
data_root = 'data/VOCdevkit/'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations', with_bbox=True),
|
||||||
|
dict(type='Resize', img_scale=(1000, 600), keep_ratio=True),
|
||||||
|
dict(type='RandomFlip', flip_ratio=0.5),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size_divisor=32),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(1000, 600),
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size_divisor=32),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=2,
|
||||||
|
workers_per_gpu=2,
|
||||||
|
train=dict(
|
||||||
|
type='RepeatDataset',
|
||||||
|
times=3,
|
||||||
|
dataset=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
ann_file=[
|
||||||
|
data_root + 'VOC2007/ImageSets/Main/trainval.txt',
|
||||||
|
data_root + 'VOC2012/ImageSets/Main/trainval.txt'
|
||||||
|
],
|
||||||
|
img_prefix=[data_root + 'VOC2007/', data_root + 'VOC2012/'],
|
||||||
|
pipeline=train_pipeline)),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt',
|
||||||
|
img_prefix=data_root + 'VOC2007/',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt',
|
||||||
|
img_prefix=data_root + 'VOC2007/',
|
||||||
|
pipeline=test_pipeline))
|
||||||
|
evaluation = dict(interval=1, metric='mAP')
|
|
@ -0,0 +1,18 @@
|
||||||
|
checkpoint_config = dict(interval=1)
|
||||||
|
# Used in MMFewShot to identify the type task, we support mmcls and mmdet now
|
||||||
|
task_type = 'mmdet'
|
||||||
|
# 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,62 @@
|
||||||
|
# model settings
|
||||||
|
model = dict(
|
||||||
|
type='FastRCNN',
|
||||||
|
pretrained='torchvision://resnet50',
|
||||||
|
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'),
|
||||||
|
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)))
|
|
@ -0,0 +1,112 @@
|
||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='BN', requires_grad=False)
|
||||||
|
model = dict(
|
||||||
|
type='FasterRCNN',
|
||||||
|
pretrained='open-mmlab://detectron2/resnet50_caffe',
|
||||||
|
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'),
|
||||||
|
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)))
|
|
@ -0,0 +1,103 @@
|
||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='BN', requires_grad=False)
|
||||||
|
model = dict(
|
||||||
|
type='FasterRCNN',
|
||||||
|
pretrained='open-mmlab://detectron2/resnet50_caffe',
|
||||||
|
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'),
|
||||||
|
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)))
|
|
@ -0,0 +1,108 @@
|
||||||
|
# model settings
|
||||||
|
model = dict(
|
||||||
|
type='FasterRCNN',
|
||||||
|
pretrained='torchvision://resnet50',
|
||||||
|
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'),
|
||||||
|
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)
|
||||||
|
))
|
|
@ -0,0 +1,60 @@
|
||||||
|
# model settings
|
||||||
|
model = dict(
|
||||||
|
type='RetinaNet',
|
||||||
|
pretrained='torchvision://resnet50',
|
||||||
|
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'),
|
||||||
|
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))
|
|
@ -0,0 +1,56 @@
|
||||||
|
# model settings
|
||||||
|
model = dict(
|
||||||
|
type='RPN',
|
||||||
|
pretrained='open-mmlab://detectron2/resnet50_caffe',
|
||||||
|
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'),
|
||||||
|
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)))
|
|
@ -0,0 +1,58 @@
|
||||||
|
# model settings
|
||||||
|
model = dict(
|
||||||
|
type='RPN',
|
||||||
|
pretrained='torchvision://resnet50',
|
||||||
|
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'),
|
||||||
|
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)))
|
|
@ -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)
|
|
@ -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=[16, 19])
|
||||||
|
runner = dict(type='EpochBasedRunner', max_epochs=20)
|
|
@ -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=[16, 22])
|
||||||
|
runner = dict(type='EpochBasedRunner', max_epochs=24)
|
|
@ -0,0 +1,10 @@
|
||||||
|
_base_ = [
|
||||||
|
'../_base_/models/faster_rcnn_r50_fpn.py',
|
||||||
|
'../_base_/datasets/coco_detection.py',
|
||||||
|
'../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
|
||||||
|
]
|
||||||
|
|
||||||
|
model = dict(type='TestDetection')
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=1
|
||||||
|
)
|
|
@ -0,0 +1,30 @@
|
||||||
|
import mmcv
|
||||||
|
|
||||||
|
from .builders import * # noqa: F401, F403
|
||||||
|
from .detection import * # noqa: F401, F403
|
||||||
|
from .version import __version__, short_version
|
||||||
|
|
||||||
|
|
||||||
|
def digit_version(version_str):
|
||||||
|
digit_version = []
|
||||||
|
for x in version_str.split('.'):
|
||||||
|
if x.isdigit():
|
||||||
|
digit_version.append(int(x))
|
||||||
|
elif x.find('rc') != -1:
|
||||||
|
patch_version = x.split('rc')
|
||||||
|
digit_version.append(int(patch_version[0]) - 1)
|
||||||
|
digit_version.append(int(patch_version[1]))
|
||||||
|
return digit_version
|
||||||
|
|
||||||
|
|
||||||
|
mmcv_minimum_version = '1.3.2'
|
||||||
|
mmcv_maximum_version = '1.4.0'
|
||||||
|
mmcv_version = digit_version(mmcv.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
assert (mmcv_version >= digit_version(mmcv_minimum_version)
|
||||||
|
and mmcv_version <= digit_version(mmcv_maximum_version)), \
|
||||||
|
f'MMCV=={mmcv.__version__} is used but incompatible. ' \
|
||||||
|
f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.'
|
||||||
|
|
||||||
|
__all__ = ['__version__', 'short_version']
|
|
@ -0,0 +1,10 @@
|
||||||
|
from .inference import (async_inference_detector, inference_detector,
|
||||||
|
init_detector, show_result_pyplot)
|
||||||
|
from .test import multi_gpu_test, single_gpu_test
|
||||||
|
from .train import get_root_logger, set_random_seed, train_model
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'get_root_logger', 'set_random_seed', 'train_model', 'init_detector',
|
||||||
|
'async_inference_detector', 'inference_detector', 'show_result_pyplot',
|
||||||
|
'multi_gpu_test', 'single_gpu_test'
|
||||||
|
]
|
|
@ -0,0 +1,239 @@
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import mmcv
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
from mmcv.ops import RoIPool
|
||||||
|
from mmcv.parallel import collate, scatter
|
||||||
|
from mmcv.runner import load_checkpoint
|
||||||
|
from mmdet.core import get_classes
|
||||||
|
from mmdet.datasets import replace_ImageToTensor
|
||||||
|
from mmdet.datasets.pipelines import Compose
|
||||||
|
from mmdet.models import build_detector
|
||||||
|
|
||||||
|
|
||||||
|
def init_detector(config, checkpoint=None, device='cuda:0', cfg_options=None):
|
||||||
|
"""Initialize a detector from config file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config (str or :obj:`mmcv.Config`): Config file path or the config
|
||||||
|
object.
|
||||||
|
checkpoint (str, optional): Checkpoint path. If left as None, the model
|
||||||
|
will not load any weights.
|
||||||
|
cfg_options (dict): Options to override some settings in the used
|
||||||
|
config.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
nn.Module: The constructed detector.
|
||||||
|
"""
|
||||||
|
if isinstance(config, str):
|
||||||
|
config = mmcv.Config.fromfile(config)
|
||||||
|
elif not isinstance(config, mmcv.Config):
|
||||||
|
raise TypeError('config must be a filename or Config object, '
|
||||||
|
f'but got {type(config)}')
|
||||||
|
if cfg_options is not None:
|
||||||
|
config.merge_from_dict(cfg_options)
|
||||||
|
config.model.pretrained = None
|
||||||
|
config.model.train_cfg = None
|
||||||
|
model = build_detector(config.model, test_cfg=config.get('test_cfg'))
|
||||||
|
if checkpoint is not None:
|
||||||
|
map_loc = 'cpu' if device == 'cpu' else None
|
||||||
|
checkpoint = load_checkpoint(model, checkpoint, map_location=map_loc)
|
||||||
|
if 'CLASSES' in checkpoint.get('meta', {}):
|
||||||
|
model.CLASSES = checkpoint['meta']['CLASSES']
|
||||||
|
else:
|
||||||
|
warnings.simplefilter('once')
|
||||||
|
warnings.warn('Class names are not saved in the checkpoint\'s '
|
||||||
|
'meta data, use COCO classes by default.')
|
||||||
|
model.CLASSES = get_classes('coco')
|
||||||
|
model.cfg = config # save the config in the model for convenience
|
||||||
|
model.to(device)
|
||||||
|
model.eval()
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
class LoadImage(object):
|
||||||
|
"""Deprecated.
|
||||||
|
|
||||||
|
A simple pipeline to load image.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __call__(self, results):
|
||||||
|
"""Call function to load images into results.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
results (dict): A result dict contains the file name
|
||||||
|
of the image to be read.
|
||||||
|
Returns:
|
||||||
|
dict: ``results`` will be returned containing loaded image.
|
||||||
|
"""
|
||||||
|
warnings.simplefilter('once')
|
||||||
|
warnings.warn('`LoadImage` is deprecated and will be removed in '
|
||||||
|
'future releases. You may use `LoadImageFromWebcam` '
|
||||||
|
'from `mmdet.datasets.pipelines.` instead.')
|
||||||
|
if isinstance(results['img'], str):
|
||||||
|
results['filename'] = results['img']
|
||||||
|
results['ori_filename'] = results['img']
|
||||||
|
else:
|
||||||
|
results['filename'] = None
|
||||||
|
results['ori_filename'] = None
|
||||||
|
img = mmcv.imread(results['img'])
|
||||||
|
results['img'] = img
|
||||||
|
results['img_fields'] = ['img']
|
||||||
|
results['img_shape'] = img.shape
|
||||||
|
results['ori_shape'] = img.shape
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def inference_detector(model, imgs):
|
||||||
|
"""Inference image(s) with the detector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (nn.Module): The loaded detector.
|
||||||
|
imgs (str/ndarray or list[str/ndarray] or tuple[str/ndarray]):
|
||||||
|
Either image files or loaded images.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
If imgs is a list or tuple, the same length list type results
|
||||||
|
will be returned, otherwise return the detection results directly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(imgs, (list, tuple)):
|
||||||
|
is_batch = True
|
||||||
|
else:
|
||||||
|
imgs = [imgs]
|
||||||
|
is_batch = False
|
||||||
|
|
||||||
|
cfg = model.cfg
|
||||||
|
device = next(model.parameters()).device # model device
|
||||||
|
|
||||||
|
if isinstance(imgs[0], np.ndarray):
|
||||||
|
cfg = cfg.copy()
|
||||||
|
# set loading pipeline type
|
||||||
|
cfg.data.test.pipeline[0].type = 'LoadImageFromWebcam'
|
||||||
|
|
||||||
|
cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline)
|
||||||
|
test_pipeline = Compose(cfg.data.test.pipeline)
|
||||||
|
|
||||||
|
datas = []
|
||||||
|
for img in imgs:
|
||||||
|
# prepare data
|
||||||
|
if isinstance(img, np.ndarray):
|
||||||
|
# directly add img
|
||||||
|
data = dict(img=img)
|
||||||
|
else:
|
||||||
|
# add information into dict
|
||||||
|
data = dict(img_info=dict(filename=img), img_prefix=None)
|
||||||
|
# build the data pipeline
|
||||||
|
data = test_pipeline(data)
|
||||||
|
datas.append(data)
|
||||||
|
|
||||||
|
data = collate(datas, samples_per_gpu=len(imgs))
|
||||||
|
# just get the actual data from DataContainer
|
||||||
|
data['img_metas'] = [img_metas.data[0] for img_metas in data['img_metas']]
|
||||||
|
data['img'] = [img.data[0] for img in data['img']]
|
||||||
|
if next(model.parameters()).is_cuda:
|
||||||
|
# scatter to specified GPU
|
||||||
|
data = scatter(data, [device])[0]
|
||||||
|
else:
|
||||||
|
for m in model.modules():
|
||||||
|
assert not isinstance(
|
||||||
|
m, RoIPool
|
||||||
|
), 'CPU inference with RoIPool is not supported currently.'
|
||||||
|
|
||||||
|
# forward the model
|
||||||
|
with torch.no_grad():
|
||||||
|
results = model(return_loss=False, rescale=True, **data)
|
||||||
|
|
||||||
|
if not is_batch:
|
||||||
|
return results[0]
|
||||||
|
else:
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
async def async_inference_detector(model, imgs):
|
||||||
|
"""Async inference image(s) with the detector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (nn.Module): The loaded detector.
|
||||||
|
img (str | ndarray): Either image files or loaded images.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Awaitable detection results.
|
||||||
|
"""
|
||||||
|
if not isinstance(imgs, (list, tuple)):
|
||||||
|
imgs = [imgs]
|
||||||
|
|
||||||
|
cfg = model.cfg
|
||||||
|
device = next(model.parameters()).device # model device
|
||||||
|
|
||||||
|
if isinstance(imgs[0], np.ndarray):
|
||||||
|
cfg = cfg.copy()
|
||||||
|
# set loading pipeline type
|
||||||
|
cfg.data.test.pipeline[0].type = 'LoadImageFromWebcam'
|
||||||
|
|
||||||
|
cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline)
|
||||||
|
test_pipeline = Compose(cfg.data.test.pipeline)
|
||||||
|
|
||||||
|
datas = []
|
||||||
|
for img in imgs:
|
||||||
|
# prepare data
|
||||||
|
if isinstance(img, np.ndarray):
|
||||||
|
# directly add img
|
||||||
|
data = dict(img=img)
|
||||||
|
else:
|
||||||
|
# add information into dict
|
||||||
|
data = dict(img_info=dict(filename=img), img_prefix=None)
|
||||||
|
# build the data pipeline
|
||||||
|
data = test_pipeline(data)
|
||||||
|
datas.append(data)
|
||||||
|
|
||||||
|
data = collate(datas, samples_per_gpu=len(imgs))
|
||||||
|
# just get the actual data from DataContainer
|
||||||
|
data['img_metas'] = [img_metas.data[0] for img_metas in data['img_metas']]
|
||||||
|
data['img'] = [img.data[0] for img in data['img']]
|
||||||
|
if next(model.parameters()).is_cuda:
|
||||||
|
# scatter to specified GPU
|
||||||
|
data = scatter(data, [device])[0]
|
||||||
|
else:
|
||||||
|
for m in model.modules():
|
||||||
|
assert not isinstance(
|
||||||
|
m, RoIPool
|
||||||
|
), 'CPU inference with RoIPool is not supported currently.'
|
||||||
|
|
||||||
|
# We don't restore `torch.is_grad_enabled()` value during concurrent
|
||||||
|
# inference since execution can overlap
|
||||||
|
torch.set_grad_enabled(False)
|
||||||
|
results = await model.aforward_test(rescale=True, **data)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def show_result_pyplot(model,
|
||||||
|
img,
|
||||||
|
result,
|
||||||
|
score_thr=0.3,
|
||||||
|
title='result',
|
||||||
|
wait_time=0):
|
||||||
|
"""Visualize the detection results on the image.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (nn.Module): The loaded detector.
|
||||||
|
img (str or np.ndarray): Image filename or loaded image.
|
||||||
|
result (tuple[list] or list): The detection result, can be either
|
||||||
|
(bbox, segm) or just bbox.
|
||||||
|
score_thr (float): The threshold to visualize the bboxes and masks.
|
||||||
|
title (str): Title of the pyplot figure.
|
||||||
|
wait_time (float): Value of waitKey param.
|
||||||
|
Default: 0.
|
||||||
|
"""
|
||||||
|
if hasattr(model, 'module'):
|
||||||
|
model = model.module
|
||||||
|
model.show_result(
|
||||||
|
img,
|
||||||
|
result,
|
||||||
|
score_thr=score_thr,
|
||||||
|
show=True,
|
||||||
|
wait_time=wait_time,
|
||||||
|
win_name=title,
|
||||||
|
bbox_color=(72, 101, 241),
|
||||||
|
text_color=(72, 101, 241))
|
|
@ -0,0 +1,189 @@
|
||||||
|
import os.path as osp
|
||||||
|
import pickle
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
import mmcv
|
||||||
|
import torch
|
||||||
|
import torch.distributed as dist
|
||||||
|
from mmcv.image import tensor2imgs
|
||||||
|
from mmcv.runner import get_dist_info
|
||||||
|
from mmdet.core import encode_mask_results
|
||||||
|
|
||||||
|
|
||||||
|
def single_gpu_test(model,
|
||||||
|
data_loader,
|
||||||
|
show=False,
|
||||||
|
out_dir=None,
|
||||||
|
show_score_thr=0.3):
|
||||||
|
model.eval()
|
||||||
|
results = []
|
||||||
|
dataset = data_loader.dataset
|
||||||
|
prog_bar = mmcv.ProgressBar(len(dataset))
|
||||||
|
for i, data in enumerate(data_loader):
|
||||||
|
with torch.no_grad():
|
||||||
|
result = model(return_loss=False, rescale=True, **data)
|
||||||
|
|
||||||
|
batch_size = len(result)
|
||||||
|
if show or out_dir:
|
||||||
|
if batch_size == 1 and isinstance(data['img'][0], torch.Tensor):
|
||||||
|
img_tensor = data['img'][0]
|
||||||
|
else:
|
||||||
|
img_tensor = data['img'][0].data[0]
|
||||||
|
img_metas = data['img_metas'][0].data[0]
|
||||||
|
imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg'])
|
||||||
|
assert len(imgs) == len(img_metas)
|
||||||
|
|
||||||
|
for i, (img, img_meta) in enumerate(zip(imgs, img_metas)):
|
||||||
|
h, w, _ = img_meta['img_shape']
|
||||||
|
img_show = img[:h, :w, :]
|
||||||
|
|
||||||
|
ori_h, ori_w = img_meta['ori_shape'][:-1]
|
||||||
|
img_show = mmcv.imresize(img_show, (ori_w, ori_h))
|
||||||
|
|
||||||
|
if out_dir:
|
||||||
|
out_file = osp.join(out_dir, img_meta['ori_filename'])
|
||||||
|
else:
|
||||||
|
out_file = None
|
||||||
|
|
||||||
|
model.module.show_result(
|
||||||
|
img_show,
|
||||||
|
result[i],
|
||||||
|
show=show,
|
||||||
|
out_file=out_file,
|
||||||
|
score_thr=show_score_thr)
|
||||||
|
|
||||||
|
# encode mask results
|
||||||
|
if isinstance(result[0], tuple):
|
||||||
|
result = [(bbox_results, encode_mask_results(mask_results))
|
||||||
|
for bbox_results, mask_results in result]
|
||||||
|
results.extend(result)
|
||||||
|
|
||||||
|
for _ in range(batch_size):
|
||||||
|
prog_bar.update()
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False):
|
||||||
|
"""Test model with multiple gpus.
|
||||||
|
|
||||||
|
This method tests model with multiple gpus and collects the results
|
||||||
|
under two different modes: gpu and cpu modes. By setting 'gpu_collect=True'
|
||||||
|
it encodes results to gpu tensors and use gpu communication for results
|
||||||
|
collection. On cpu mode it saves the results on different gpus to 'tmpdir'
|
||||||
|
and collects them by the rank 0 worker.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (nn.Module): Model to be tested.
|
||||||
|
data_loader (nn.Dataloader): Pytorch data loader.
|
||||||
|
tmpdir (str): Path of directory to save the temporary results from
|
||||||
|
different gpus under cpu mode.
|
||||||
|
gpu_collect (bool): Option to use either gpu or cpu to collect results.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: The prediction results.
|
||||||
|
"""
|
||||||
|
model.eval()
|
||||||
|
results = []
|
||||||
|
dataset = data_loader.dataset
|
||||||
|
rank, world_size = get_dist_info()
|
||||||
|
if rank == 0:
|
||||||
|
prog_bar = mmcv.ProgressBar(len(dataset))
|
||||||
|
time.sleep(2) # This line can prevent deadlock problem in some cases.
|
||||||
|
for i, data in enumerate(data_loader):
|
||||||
|
with torch.no_grad():
|
||||||
|
result = model(return_loss=False, rescale=True, **data)
|
||||||
|
# encode mask results
|
||||||
|
if isinstance(result[0], tuple):
|
||||||
|
result = [(bbox_results, encode_mask_results(mask_results))
|
||||||
|
for bbox_results, mask_results in result]
|
||||||
|
results.extend(result)
|
||||||
|
|
||||||
|
if rank == 0:
|
||||||
|
batch_size = len(result)
|
||||||
|
for _ in range(batch_size * world_size):
|
||||||
|
prog_bar.update()
|
||||||
|
|
||||||
|
# collect results from all ranks
|
||||||
|
if gpu_collect:
|
||||||
|
results = collect_results_gpu(results, len(dataset))
|
||||||
|
else:
|
||||||
|
results = collect_results_cpu(results, len(dataset), tmpdir)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def collect_results_cpu(result_part, size, tmpdir=None):
|
||||||
|
rank, world_size = get_dist_info()
|
||||||
|
# create a tmp dir if it is not specified
|
||||||
|
if tmpdir is None:
|
||||||
|
MAX_LEN = 512
|
||||||
|
# 32 is whitespace
|
||||||
|
dir_tensor = torch.full((MAX_LEN, ),
|
||||||
|
32,
|
||||||
|
dtype=torch.uint8,
|
||||||
|
device='cuda')
|
||||||
|
if rank == 0:
|
||||||
|
mmcv.mkdir_or_exist('.dist_test')
|
||||||
|
tmpdir = tempfile.mkdtemp(dir='.dist_test')
|
||||||
|
tmpdir = torch.tensor(
|
||||||
|
bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda')
|
||||||
|
dir_tensor[:len(tmpdir)] = tmpdir
|
||||||
|
dist.broadcast(dir_tensor, 0)
|
||||||
|
tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip()
|
||||||
|
else:
|
||||||
|
mmcv.mkdir_or_exist(tmpdir)
|
||||||
|
# dump the part result to the dir
|
||||||
|
mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl'))
|
||||||
|
dist.barrier()
|
||||||
|
# collect all parts
|
||||||
|
if rank != 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
# load results of all parts from tmp dir
|
||||||
|
part_list = []
|
||||||
|
for i in range(world_size):
|
||||||
|
part_file = osp.join(tmpdir, f'part_{i}.pkl')
|
||||||
|
part_list.append(mmcv.load(part_file))
|
||||||
|
# sort the results
|
||||||
|
ordered_results = []
|
||||||
|
for res in zip(*part_list):
|
||||||
|
ordered_results.extend(list(res))
|
||||||
|
# the dataloader may pad some samples
|
||||||
|
ordered_results = ordered_results[:size]
|
||||||
|
# remove tmp dir
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
return ordered_results
|
||||||
|
|
||||||
|
|
||||||
|
def collect_results_gpu(result_part, size):
|
||||||
|
rank, world_size = get_dist_info()
|
||||||
|
# dump result part to tensor with pickle
|
||||||
|
part_tensor = torch.tensor(
|
||||||
|
bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda')
|
||||||
|
# gather all result part tensor shape
|
||||||
|
shape_tensor = torch.tensor(part_tensor.shape, device='cuda')
|
||||||
|
shape_list = [shape_tensor.clone() for _ in range(world_size)]
|
||||||
|
dist.all_gather(shape_list, shape_tensor)
|
||||||
|
# padding result part tensor to max length
|
||||||
|
shape_max = torch.tensor(shape_list).max()
|
||||||
|
part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda')
|
||||||
|
part_send[:shape_tensor[0]] = part_tensor
|
||||||
|
part_recv_list = [
|
||||||
|
part_tensor.new_zeros(shape_max) for _ in range(world_size)
|
||||||
|
]
|
||||||
|
# gather all result part
|
||||||
|
dist.all_gather(part_recv_list, part_send)
|
||||||
|
|
||||||
|
if rank == 0:
|
||||||
|
part_list = []
|
||||||
|
for recv, shape in zip(part_recv_list, shape_list):
|
||||||
|
part_list.append(
|
||||||
|
pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()))
|
||||||
|
# sort the results
|
||||||
|
ordered_results = []
|
||||||
|
for res in zip(*part_list):
|
||||||
|
ordered_results.extend(list(res))
|
||||||
|
# the dataloader may pad some samples
|
||||||
|
ordered_results = ordered_results[:size]
|
||||||
|
return ordered_results
|
|
@ -0,0 +1,169 @@
|
||||||
|
import random
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
from mmcv.parallel import MMDataParallel, MMDistributedDataParallel
|
||||||
|
from mmcv.runner import (HOOKS, DistSamplerSeedHook, EpochBasedRunner,
|
||||||
|
Fp16OptimizerHook, OptimizerHook, build_optimizer,
|
||||||
|
build_runner)
|
||||||
|
from mmcv.utils import build_from_cfg
|
||||||
|
from mmdet.core import DistEvalHook, EvalHook
|
||||||
|
from mmdet.datasets import (build_dataloader, build_dataset,
|
||||||
|
replace_ImageToTensor)
|
||||||
|
from mmdet.utils import get_root_logger
|
||||||
|
|
||||||
|
|
||||||
|
def set_random_seed(seed, deterministic=False):
|
||||||
|
"""Set random seed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seed (int): Seed to be used.
|
||||||
|
deterministic (bool): Whether to set the deterministic option for
|
||||||
|
CUDNN backend, i.e., set `torch.backends.cudnn.deterministic`
|
||||||
|
to True and `torch.backends.cudnn.benchmark` to False.
|
||||||
|
Default: False.
|
||||||
|
"""
|
||||||
|
random.seed(seed)
|
||||||
|
np.random.seed(seed)
|
||||||
|
torch.manual_seed(seed)
|
||||||
|
torch.cuda.manual_seed_all(seed)
|
||||||
|
if deterministic:
|
||||||
|
torch.backends.cudnn.deterministic = True
|
||||||
|
torch.backends.cudnn.benchmark = False
|
||||||
|
|
||||||
|
|
||||||
|
def train_model(model,
|
||||||
|
dataset,
|
||||||
|
cfg,
|
||||||
|
distributed=False,
|
||||||
|
validate=False,
|
||||||
|
timestamp=None,
|
||||||
|
meta=None):
|
||||||
|
logger = get_root_logger(cfg.log_level)
|
||||||
|
|
||||||
|
# prepare data loaders
|
||||||
|
dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset]
|
||||||
|
if 'imgs_per_gpu' in cfg.data:
|
||||||
|
logger.warning('"imgs_per_gpu" is deprecated in MMDet V2.0. '
|
||||||
|
'Please use "samples_per_gpu" instead')
|
||||||
|
if 'samples_per_gpu' in cfg.data:
|
||||||
|
logger.warning(
|
||||||
|
f'Got "imgs_per_gpu"={cfg.data.imgs_per_gpu} and '
|
||||||
|
f'"samples_per_gpu"={cfg.data.samples_per_gpu}, "imgs_per_gpu"'
|
||||||
|
f'={cfg.data.imgs_per_gpu} is used in this experiments')
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
'Automatically set "samples_per_gpu"="imgs_per_gpu"='
|
||||||
|
f'{cfg.data.imgs_per_gpu} in this experiments')
|
||||||
|
cfg.data.samples_per_gpu = cfg.data.imgs_per_gpu
|
||||||
|
|
||||||
|
data_loaders = [
|
||||||
|
build_dataloader(
|
||||||
|
ds,
|
||||||
|
cfg.data.samples_per_gpu,
|
||||||
|
cfg.data.workers_per_gpu,
|
||||||
|
# cfg.gpus will be ignored if distributed
|
||||||
|
len(cfg.gpu_ids),
|
||||||
|
dist=distributed,
|
||||||
|
seed=cfg.seed) for ds in dataset
|
||||||
|
]
|
||||||
|
|
||||||
|
# put model on gpus
|
||||||
|
if distributed:
|
||||||
|
find_unused_parameters = cfg.get('find_unused_parameters', False)
|
||||||
|
# Sets the `find_unused_parameters` parameter in
|
||||||
|
# torch.nn.parallel.DistributedDataParallel
|
||||||
|
model = MMDistributedDataParallel(
|
||||||
|
model.cuda(),
|
||||||
|
device_ids=[torch.cuda.current_device()],
|
||||||
|
broadcast_buffers=False,
|
||||||
|
find_unused_parameters=find_unused_parameters)
|
||||||
|
else:
|
||||||
|
model = MMDataParallel(
|
||||||
|
model.cuda(cfg.gpu_ids[0]), device_ids=cfg.gpu_ids)
|
||||||
|
|
||||||
|
# build runner
|
||||||
|
optimizer = build_optimizer(model, cfg.optimizer)
|
||||||
|
|
||||||
|
if 'runner' not in cfg:
|
||||||
|
cfg.runner = {
|
||||||
|
'type': 'EpochBasedRunner',
|
||||||
|
'max_epochs': cfg.total_epochs
|
||||||
|
}
|
||||||
|
warnings.warn(
|
||||||
|
'config is now expected to have a `runner` section, '
|
||||||
|
'please set `runner` in your config.', UserWarning)
|
||||||
|
else:
|
||||||
|
if 'total_epochs' in cfg:
|
||||||
|
assert cfg.total_epochs == cfg.runner.max_epochs
|
||||||
|
|
||||||
|
runner = build_runner(
|
||||||
|
cfg.runner,
|
||||||
|
default_args=dict(
|
||||||
|
model=model,
|
||||||
|
optimizer=optimizer,
|
||||||
|
work_dir=cfg.work_dir,
|
||||||
|
logger=logger,
|
||||||
|
meta=meta))
|
||||||
|
|
||||||
|
# an ugly workaround to make .log and .log.json filenames the same
|
||||||
|
runner.timestamp = timestamp
|
||||||
|
|
||||||
|
# fp16 setting
|
||||||
|
fp16_cfg = cfg.get('fp16', None)
|
||||||
|
if fp16_cfg is not None:
|
||||||
|
optimizer_config = Fp16OptimizerHook(
|
||||||
|
**cfg.optimizer_config, **fp16_cfg, distributed=distributed)
|
||||||
|
elif distributed and 'type' not in cfg.optimizer_config:
|
||||||
|
optimizer_config = OptimizerHook(**cfg.optimizer_config)
|
||||||
|
else:
|
||||||
|
optimizer_config = cfg.optimizer_config
|
||||||
|
|
||||||
|
# register hooks
|
||||||
|
runner.register_training_hooks(cfg.lr_config, optimizer_config,
|
||||||
|
cfg.checkpoint_config, cfg.log_config,
|
||||||
|
cfg.get('momentum_config', None))
|
||||||
|
if distributed:
|
||||||
|
if isinstance(runner, EpochBasedRunner):
|
||||||
|
runner.register_hook(DistSamplerSeedHook())
|
||||||
|
|
||||||
|
# register eval hooks
|
||||||
|
if validate:
|
||||||
|
# Support batch_size > 1 in validation
|
||||||
|
val_samples_per_gpu = cfg.data.val.pop('samples_per_gpu', 1)
|
||||||
|
if val_samples_per_gpu > 1:
|
||||||
|
# Replace 'ImageToTensor' to 'DefaultFormatBundle'
|
||||||
|
cfg.data.val.pipeline = replace_ImageToTensor(
|
||||||
|
cfg.data.val.pipeline)
|
||||||
|
val_dataset = build_dataset(cfg.data.val, dict(test_mode=True))
|
||||||
|
val_dataloader = build_dataloader(
|
||||||
|
val_dataset,
|
||||||
|
samples_per_gpu=val_samples_per_gpu,
|
||||||
|
workers_per_gpu=cfg.data.workers_per_gpu,
|
||||||
|
dist=distributed,
|
||||||
|
shuffle=False)
|
||||||
|
eval_cfg = cfg.get('evaluation', {})
|
||||||
|
eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner'
|
||||||
|
eval_hook = DistEvalHook if distributed else EvalHook
|
||||||
|
runner.register_hook(eval_hook(val_dataloader, **eval_cfg))
|
||||||
|
|
||||||
|
# user-defined hooks
|
||||||
|
if cfg.get('custom_hooks', None):
|
||||||
|
custom_hooks = cfg.custom_hooks
|
||||||
|
assert isinstance(custom_hooks, list), \
|
||||||
|
f'custom_hooks expect list type, but got {type(custom_hooks)}'
|
||||||
|
for hook_cfg in cfg.custom_hooks:
|
||||||
|
assert isinstance(hook_cfg, dict), \
|
||||||
|
'Each item in custom_hooks expects dict type, but got ' \
|
||||||
|
f'{type(hook_cfg)}'
|
||||||
|
hook_cfg = hook_cfg.copy()
|
||||||
|
priority = hook_cfg.pop('priority', 'NORMAL')
|
||||||
|
hook = build_from_cfg(hook_cfg, HOOKS)
|
||||||
|
runner.register_hook(hook, priority=priority)
|
||||||
|
|
||||||
|
if cfg.resume_from:
|
||||||
|
runner.resume(cfg.resume_from)
|
||||||
|
elif cfg.load_from:
|
||||||
|
runner.load_checkpoint(cfg.load_from)
|
||||||
|
runner.run(data_loaders, cfg.workflow)
|
|
@ -0,0 +1 @@
|
||||||
|
from .dataset_builder import * # noqa: F401, F403
|
|
@ -0,0 +1,22 @@
|
||||||
|
from mmcls.datasets.builder import build_dataloader as build_cls_dataloader
|
||||||
|
from mmcls.datasets.builder import build_dataset as build_cls_dataset
|
||||||
|
from mmdet.datasets.builder import build_dataloader as build_det_dataloader
|
||||||
|
from mmdet.datasets.builder import build_dataset as build_det_dataset
|
||||||
|
|
||||||
|
|
||||||
|
def build_dataloader(*args, task_type='mmdet', **kwargs):
|
||||||
|
|
||||||
|
if task_type == 'mmdet':
|
||||||
|
data_loader = build_det_dataloader(*args, **kwargs)
|
||||||
|
elif task_type == 'mmcls':
|
||||||
|
data_loader = build_cls_dataloader(*args, **kwargs)
|
||||||
|
return data_loader
|
||||||
|
|
||||||
|
|
||||||
|
def build_dataset(*args, task_type='mmdet', **kwargs):
|
||||||
|
|
||||||
|
if task_type == 'mmdet':
|
||||||
|
dataset = build_det_dataset(*args, **kwargs)
|
||||||
|
elif task_type == 'mmcls':
|
||||||
|
dataset = build_cls_dataset(*args, **kwargs)
|
||||||
|
return dataset
|
|
@ -0,0 +1,9 @@
|
||||||
|
from mmcls.models.builder import build_classifier as build_cls_model
|
||||||
|
from mmdet.models.builder import build_detector as build_det_model
|
||||||
|
|
||||||
|
|
||||||
|
def build_model(*args, task_type='mmdet', **kwargs):
|
||||||
|
if task_type == 'mmdet':
|
||||||
|
return build_det_model(*args, **kwargs)
|
||||||
|
elif task_type == 'mmcls':
|
||||||
|
return build_cls_model(*args, **kwargs)
|
|
@ -0,0 +1,2 @@
|
||||||
|
from .datasets import * # noqa: F401,F403
|
||||||
|
from .models import * # noqa: F401,F403
|
|
@ -0,0 +1,10 @@
|
||||||
|
from .inference import (async_inference_detector, inference_detector,
|
||||||
|
init_detector, show_result_pyplot)
|
||||||
|
from .test import multi_gpu_test, single_gpu_test
|
||||||
|
from .train import get_root_logger, set_random_seed, train_detector
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'get_root_logger', 'set_random_seed', 'train_detector', 'init_detector',
|
||||||
|
'async_inference_detector', 'inference_detector', 'show_result_pyplot',
|
||||||
|
'multi_gpu_test', 'single_gpu_test'
|
||||||
|
]
|
|
@ -0,0 +1,239 @@
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import mmcv
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
from mmcv.ops import RoIPool
|
||||||
|
from mmcv.parallel import collate, scatter
|
||||||
|
from mmcv.runner import load_checkpoint
|
||||||
|
from mmdet.core import get_classes
|
||||||
|
from mmdet.datasets import replace_ImageToTensor
|
||||||
|
from mmdet.datasets.pipelines import Compose
|
||||||
|
from mmdet.models import build_detector
|
||||||
|
|
||||||
|
|
||||||
|
def init_detector(config, checkpoint=None, device='cuda:0', cfg_options=None):
|
||||||
|
"""Initialize a detector from config file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config (str or :obj:`mmcv.Config`): Config file path or the config
|
||||||
|
object.
|
||||||
|
checkpoint (str, optional): Checkpoint path. If left as None, the model
|
||||||
|
will not load any weights.
|
||||||
|
cfg_options (dict): Options to override some settings in the used
|
||||||
|
config.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
nn.Module: The constructed detector.
|
||||||
|
"""
|
||||||
|
if isinstance(config, str):
|
||||||
|
config = mmcv.Config.fromfile(config)
|
||||||
|
elif not isinstance(config, mmcv.Config):
|
||||||
|
raise TypeError('config must be a filename or Config object, '
|
||||||
|
f'but got {type(config)}')
|
||||||
|
if cfg_options is not None:
|
||||||
|
config.merge_from_dict(cfg_options)
|
||||||
|
config.model.pretrained = None
|
||||||
|
config.model.train_cfg = None
|
||||||
|
model = build_detector(config.model, test_cfg=config.get('test_cfg'))
|
||||||
|
if checkpoint is not None:
|
||||||
|
map_loc = 'cpu' if device == 'cpu' else None
|
||||||
|
checkpoint = load_checkpoint(model, checkpoint, map_location=map_loc)
|
||||||
|
if 'CLASSES' in checkpoint.get('meta', {}):
|
||||||
|
model.CLASSES = checkpoint['meta']['CLASSES']
|
||||||
|
else:
|
||||||
|
warnings.simplefilter('once')
|
||||||
|
warnings.warn('Class names are not saved in the checkpoint\'s '
|
||||||
|
'meta data, use COCO classes by default.')
|
||||||
|
model.CLASSES = get_classes('coco')
|
||||||
|
model.cfg = config # save the config in the model for convenience
|
||||||
|
model.to(device)
|
||||||
|
model.eval()
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
class LoadImage(object):
|
||||||
|
"""Deprecated.
|
||||||
|
|
||||||
|
A simple pipeline to load image.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __call__(self, results):
|
||||||
|
"""Call function to load images into results.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
results (dict): A result dict contains the file name
|
||||||
|
of the image to be read.
|
||||||
|
Returns:
|
||||||
|
dict: ``results`` will be returned containing loaded image.
|
||||||
|
"""
|
||||||
|
warnings.simplefilter('once')
|
||||||
|
warnings.warn('`LoadImage` is deprecated and will be removed in '
|
||||||
|
'future releases. You may use `LoadImageFromWebcam` '
|
||||||
|
'from `mmdet.datasets.pipelines.` instead.')
|
||||||
|
if isinstance(results['img'], str):
|
||||||
|
results['filename'] = results['img']
|
||||||
|
results['ori_filename'] = results['img']
|
||||||
|
else:
|
||||||
|
results['filename'] = None
|
||||||
|
results['ori_filename'] = None
|
||||||
|
img = mmcv.imread(results['img'])
|
||||||
|
results['img'] = img
|
||||||
|
results['img_fields'] = ['img']
|
||||||
|
results['img_shape'] = img.shape
|
||||||
|
results['ori_shape'] = img.shape
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def inference_detector(model, imgs):
|
||||||
|
"""Inference image(s) with the detector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (nn.Module): The loaded detector.
|
||||||
|
imgs (str/ndarray or list[str/ndarray] or tuple[str/ndarray]):
|
||||||
|
Either image files or loaded images.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
If imgs is a list or tuple, the same length list type results
|
||||||
|
will be returned, otherwise return the detection results directly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(imgs, (list, tuple)):
|
||||||
|
is_batch = True
|
||||||
|
else:
|
||||||
|
imgs = [imgs]
|
||||||
|
is_batch = False
|
||||||
|
|
||||||
|
cfg = model.cfg
|
||||||
|
device = next(model.parameters()).device # model device
|
||||||
|
|
||||||
|
if isinstance(imgs[0], np.ndarray):
|
||||||
|
cfg = cfg.copy()
|
||||||
|
# set loading pipeline type
|
||||||
|
cfg.data.test.pipeline[0].type = 'LoadImageFromWebcam'
|
||||||
|
|
||||||
|
cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline)
|
||||||
|
test_pipeline = Compose(cfg.data.test.pipeline)
|
||||||
|
|
||||||
|
datas = []
|
||||||
|
for img in imgs:
|
||||||
|
# prepare data
|
||||||
|
if isinstance(img, np.ndarray):
|
||||||
|
# directly add img
|
||||||
|
data = dict(img=img)
|
||||||
|
else:
|
||||||
|
# add information into dict
|
||||||
|
data = dict(img_info=dict(filename=img), img_prefix=None)
|
||||||
|
# build the data pipeline
|
||||||
|
data = test_pipeline(data)
|
||||||
|
datas.append(data)
|
||||||
|
|
||||||
|
data = collate(datas, samples_per_gpu=len(imgs))
|
||||||
|
# just get the actual data from DataContainer
|
||||||
|
data['img_metas'] = [img_metas.data[0] for img_metas in data['img_metas']]
|
||||||
|
data['img'] = [img.data[0] for img in data['img']]
|
||||||
|
if next(model.parameters()).is_cuda:
|
||||||
|
# scatter to specified GPU
|
||||||
|
data = scatter(data, [device])[0]
|
||||||
|
else:
|
||||||
|
for m in model.modules():
|
||||||
|
assert not isinstance(
|
||||||
|
m, RoIPool
|
||||||
|
), 'CPU inference with RoIPool is not supported currently.'
|
||||||
|
|
||||||
|
# forward the model
|
||||||
|
with torch.no_grad():
|
||||||
|
results = model(return_loss=False, rescale=True, **data)
|
||||||
|
|
||||||
|
if not is_batch:
|
||||||
|
return results[0]
|
||||||
|
else:
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
async def async_inference_detector(model, imgs):
|
||||||
|
"""Async inference image(s) with the detector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (nn.Module): The loaded detector.
|
||||||
|
img (str | ndarray): Either image files or loaded images.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Awaitable detection results.
|
||||||
|
"""
|
||||||
|
if not isinstance(imgs, (list, tuple)):
|
||||||
|
imgs = [imgs]
|
||||||
|
|
||||||
|
cfg = model.cfg
|
||||||
|
device = next(model.parameters()).device # model device
|
||||||
|
|
||||||
|
if isinstance(imgs[0], np.ndarray):
|
||||||
|
cfg = cfg.copy()
|
||||||
|
# set loading pipeline type
|
||||||
|
cfg.data.test.pipeline[0].type = 'LoadImageFromWebcam'
|
||||||
|
|
||||||
|
cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline)
|
||||||
|
test_pipeline = Compose(cfg.data.test.pipeline)
|
||||||
|
|
||||||
|
datas = []
|
||||||
|
for img in imgs:
|
||||||
|
# prepare data
|
||||||
|
if isinstance(img, np.ndarray):
|
||||||
|
# directly add img
|
||||||
|
data = dict(img=img)
|
||||||
|
else:
|
||||||
|
# add information into dict
|
||||||
|
data = dict(img_info=dict(filename=img), img_prefix=None)
|
||||||
|
# build the data pipeline
|
||||||
|
data = test_pipeline(data)
|
||||||
|
datas.append(data)
|
||||||
|
|
||||||
|
data = collate(datas, samples_per_gpu=len(imgs))
|
||||||
|
# just get the actual data from DataContainer
|
||||||
|
data['img_metas'] = [img_metas.data[0] for img_metas in data['img_metas']]
|
||||||
|
data['img'] = [img.data[0] for img in data['img']]
|
||||||
|
if next(model.parameters()).is_cuda:
|
||||||
|
# scatter to specified GPU
|
||||||
|
data = scatter(data, [device])[0]
|
||||||
|
else:
|
||||||
|
for m in model.modules():
|
||||||
|
assert not isinstance(
|
||||||
|
m, RoIPool
|
||||||
|
), 'CPU inference with RoIPool is not supported currently.'
|
||||||
|
|
||||||
|
# We don't restore `torch.is_grad_enabled()` value during concurrent
|
||||||
|
# inference since execution can overlap
|
||||||
|
torch.set_grad_enabled(False)
|
||||||
|
results = await model.aforward_test(rescale=True, **data)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def show_result_pyplot(model,
|
||||||
|
img,
|
||||||
|
result,
|
||||||
|
score_thr=0.3,
|
||||||
|
title='result',
|
||||||
|
wait_time=0):
|
||||||
|
"""Visualize the detection results on the image.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (nn.Module): The loaded detector.
|
||||||
|
img (str or np.ndarray): Image filename or loaded image.
|
||||||
|
result (tuple[list] or list): The detection result, can be either
|
||||||
|
(bbox, segm) or just bbox.
|
||||||
|
score_thr (float): The threshold to visualize the bboxes and masks.
|
||||||
|
title (str): Title of the pyplot figure.
|
||||||
|
wait_time (float): Value of waitKey param.
|
||||||
|
Default: 0.
|
||||||
|
"""
|
||||||
|
if hasattr(model, 'module'):
|
||||||
|
model = model.module
|
||||||
|
model.show_result(
|
||||||
|
img,
|
||||||
|
result,
|
||||||
|
score_thr=score_thr,
|
||||||
|
show=True,
|
||||||
|
wait_time=wait_time,
|
||||||
|
win_name=title,
|
||||||
|
bbox_color=(72, 101, 241),
|
||||||
|
text_color=(72, 101, 241))
|
|
@ -0,0 +1,189 @@
|
||||||
|
import os.path as osp
|
||||||
|
import pickle
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
import mmcv
|
||||||
|
import torch
|
||||||
|
import torch.distributed as dist
|
||||||
|
from mmcv.image import tensor2imgs
|
||||||
|
from mmcv.runner import get_dist_info
|
||||||
|
from mmdet.core import encode_mask_results
|
||||||
|
|
||||||
|
|
||||||
|
def single_gpu_test(model,
|
||||||
|
data_loader,
|
||||||
|
show=False,
|
||||||
|
out_dir=None,
|
||||||
|
show_score_thr=0.3):
|
||||||
|
model.eval()
|
||||||
|
results = []
|
||||||
|
dataset = data_loader.dataset
|
||||||
|
prog_bar = mmcv.ProgressBar(len(dataset))
|
||||||
|
for i, data in enumerate(data_loader):
|
||||||
|
with torch.no_grad():
|
||||||
|
result = model(return_loss=False, rescale=True, **data)
|
||||||
|
|
||||||
|
batch_size = len(result)
|
||||||
|
if show or out_dir:
|
||||||
|
if batch_size == 1 and isinstance(data['img'][0], torch.Tensor):
|
||||||
|
img_tensor = data['img'][0]
|
||||||
|
else:
|
||||||
|
img_tensor = data['img'][0].data[0]
|
||||||
|
img_metas = data['img_metas'][0].data[0]
|
||||||
|
imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg'])
|
||||||
|
assert len(imgs) == len(img_metas)
|
||||||
|
|
||||||
|
for i, (img, img_meta) in enumerate(zip(imgs, img_metas)):
|
||||||
|
h, w, _ = img_meta['img_shape']
|
||||||
|
img_show = img[:h, :w, :]
|
||||||
|
|
||||||
|
ori_h, ori_w = img_meta['ori_shape'][:-1]
|
||||||
|
img_show = mmcv.imresize(img_show, (ori_w, ori_h))
|
||||||
|
|
||||||
|
if out_dir:
|
||||||
|
out_file = osp.join(out_dir, img_meta['ori_filename'])
|
||||||
|
else:
|
||||||
|
out_file = None
|
||||||
|
|
||||||
|
model.module.show_result(
|
||||||
|
img_show,
|
||||||
|
result[i],
|
||||||
|
show=show,
|
||||||
|
out_file=out_file,
|
||||||
|
score_thr=show_score_thr)
|
||||||
|
|
||||||
|
# encode mask results
|
||||||
|
if isinstance(result[0], tuple):
|
||||||
|
result = [(bbox_results, encode_mask_results(mask_results))
|
||||||
|
for bbox_results, mask_results in result]
|
||||||
|
results.extend(result)
|
||||||
|
|
||||||
|
for _ in range(batch_size):
|
||||||
|
prog_bar.update()
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False):
|
||||||
|
"""Test model with multiple gpus.
|
||||||
|
|
||||||
|
This method tests model with multiple gpus and collects the results
|
||||||
|
under two different modes: gpu and cpu modes. By setting 'gpu_collect=True'
|
||||||
|
it encodes results to gpu tensors and use gpu communication for results
|
||||||
|
collection. On cpu mode it saves the results on different gpus to 'tmpdir'
|
||||||
|
and collects them by the rank 0 worker.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model (nn.Module): Model to be tested.
|
||||||
|
data_loader (nn.Dataloader): Pytorch data loader.
|
||||||
|
tmpdir (str): Path of directory to save the temporary results from
|
||||||
|
different gpus under cpu mode.
|
||||||
|
gpu_collect (bool): Option to use either gpu or cpu to collect results.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: The prediction results.
|
||||||
|
"""
|
||||||
|
model.eval()
|
||||||
|
results = []
|
||||||
|
dataset = data_loader.dataset
|
||||||
|
rank, world_size = get_dist_info()
|
||||||
|
if rank == 0:
|
||||||
|
prog_bar = mmcv.ProgressBar(len(dataset))
|
||||||
|
time.sleep(2) # This line can prevent deadlock problem in some cases.
|
||||||
|
for i, data in enumerate(data_loader):
|
||||||
|
with torch.no_grad():
|
||||||
|
result = model(return_loss=False, rescale=True, **data)
|
||||||
|
# encode mask results
|
||||||
|
if isinstance(result[0], tuple):
|
||||||
|
result = [(bbox_results, encode_mask_results(mask_results))
|
||||||
|
for bbox_results, mask_results in result]
|
||||||
|
results.extend(result)
|
||||||
|
|
||||||
|
if rank == 0:
|
||||||
|
batch_size = len(result)
|
||||||
|
for _ in range(batch_size * world_size):
|
||||||
|
prog_bar.update()
|
||||||
|
|
||||||
|
# collect results from all ranks
|
||||||
|
if gpu_collect:
|
||||||
|
results = collect_results_gpu(results, len(dataset))
|
||||||
|
else:
|
||||||
|
results = collect_results_cpu(results, len(dataset), tmpdir)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def collect_results_cpu(result_part, size, tmpdir=None):
|
||||||
|
rank, world_size = get_dist_info()
|
||||||
|
# create a tmp dir if it is not specified
|
||||||
|
if tmpdir is None:
|
||||||
|
MAX_LEN = 512
|
||||||
|
# 32 is whitespace
|
||||||
|
dir_tensor = torch.full((MAX_LEN, ),
|
||||||
|
32,
|
||||||
|
dtype=torch.uint8,
|
||||||
|
device='cuda')
|
||||||
|
if rank == 0:
|
||||||
|
mmcv.mkdir_or_exist('.dist_test')
|
||||||
|
tmpdir = tempfile.mkdtemp(dir='.dist_test')
|
||||||
|
tmpdir = torch.tensor(
|
||||||
|
bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda')
|
||||||
|
dir_tensor[:len(tmpdir)] = tmpdir
|
||||||
|
dist.broadcast(dir_tensor, 0)
|
||||||
|
tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip()
|
||||||
|
else:
|
||||||
|
mmcv.mkdir_or_exist(tmpdir)
|
||||||
|
# dump the part result to the dir
|
||||||
|
mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl'))
|
||||||
|
dist.barrier()
|
||||||
|
# collect all parts
|
||||||
|
if rank != 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
# load results of all parts from tmp dir
|
||||||
|
part_list = []
|
||||||
|
for i in range(world_size):
|
||||||
|
part_file = osp.join(tmpdir, f'part_{i}.pkl')
|
||||||
|
part_list.append(mmcv.load(part_file))
|
||||||
|
# sort the results
|
||||||
|
ordered_results = []
|
||||||
|
for res in zip(*part_list):
|
||||||
|
ordered_results.extend(list(res))
|
||||||
|
# the dataloader may pad some samples
|
||||||
|
ordered_results = ordered_results[:size]
|
||||||
|
# remove tmp dir
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
return ordered_results
|
||||||
|
|
||||||
|
|
||||||
|
def collect_results_gpu(result_part, size):
|
||||||
|
rank, world_size = get_dist_info()
|
||||||
|
# dump result part to tensor with pickle
|
||||||
|
part_tensor = torch.tensor(
|
||||||
|
bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda')
|
||||||
|
# gather all result part tensor shape
|
||||||
|
shape_tensor = torch.tensor(part_tensor.shape, device='cuda')
|
||||||
|
shape_list = [shape_tensor.clone() for _ in range(world_size)]
|
||||||
|
dist.all_gather(shape_list, shape_tensor)
|
||||||
|
# padding result part tensor to max length
|
||||||
|
shape_max = torch.tensor(shape_list).max()
|
||||||
|
part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda')
|
||||||
|
part_send[:shape_tensor[0]] = part_tensor
|
||||||
|
part_recv_list = [
|
||||||
|
part_tensor.new_zeros(shape_max) for _ in range(world_size)
|
||||||
|
]
|
||||||
|
# gather all result part
|
||||||
|
dist.all_gather(part_recv_list, part_send)
|
||||||
|
|
||||||
|
if rank == 0:
|
||||||
|
part_list = []
|
||||||
|
for recv, shape in zip(part_recv_list, shape_list):
|
||||||
|
part_list.append(
|
||||||
|
pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()))
|
||||||
|
# sort the results
|
||||||
|
ordered_results = []
|
||||||
|
for res in zip(*part_list):
|
||||||
|
ordered_results.extend(list(res))
|
||||||
|
# the dataloader may pad some samples
|
||||||
|
ordered_results = ordered_results[:size]
|
||||||
|
return ordered_results
|
|
@ -0,0 +1,169 @@
|
||||||
|
import random
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
from mmcv.parallel import MMDataParallel, MMDistributedDataParallel
|
||||||
|
from mmcv.runner import (HOOKS, DistSamplerSeedHook, EpochBasedRunner,
|
||||||
|
Fp16OptimizerHook, OptimizerHook, build_optimizer,
|
||||||
|
build_runner)
|
||||||
|
from mmcv.utils import build_from_cfg
|
||||||
|
from mmdet.core import DistEvalHook, EvalHook
|
||||||
|
from mmdet.datasets import (build_dataloader, build_dataset,
|
||||||
|
replace_ImageToTensor)
|
||||||
|
from mmdet.utils import get_root_logger
|
||||||
|
|
||||||
|
|
||||||
|
def set_random_seed(seed, deterministic=False):
|
||||||
|
"""Set random seed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seed (int): Seed to be used.
|
||||||
|
deterministic (bool): Whether to set the deterministic option for
|
||||||
|
CUDNN backend, i.e., set `torch.backends.cudnn.deterministic`
|
||||||
|
to True and `torch.backends.cudnn.benchmark` to False.
|
||||||
|
Default: False.
|
||||||
|
"""
|
||||||
|
random.seed(seed)
|
||||||
|
np.random.seed(seed)
|
||||||
|
torch.manual_seed(seed)
|
||||||
|
torch.cuda.manual_seed_all(seed)
|
||||||
|
if deterministic:
|
||||||
|
torch.backends.cudnn.deterministic = True
|
||||||
|
torch.backends.cudnn.benchmark = False
|
||||||
|
|
||||||
|
|
||||||
|
def train_detector(model,
|
||||||
|
dataset,
|
||||||
|
cfg,
|
||||||
|
distributed=False,
|
||||||
|
validate=False,
|
||||||
|
timestamp=None,
|
||||||
|
meta=None):
|
||||||
|
logger = get_root_logger(cfg.log_level)
|
||||||
|
|
||||||
|
# prepare data loaders
|
||||||
|
dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset]
|
||||||
|
if 'imgs_per_gpu' in cfg.data:
|
||||||
|
logger.warning('"imgs_per_gpu" is deprecated in MMDet V2.0. '
|
||||||
|
'Please use "samples_per_gpu" instead')
|
||||||
|
if 'samples_per_gpu' in cfg.data:
|
||||||
|
logger.warning(
|
||||||
|
f'Got "imgs_per_gpu"={cfg.data.imgs_per_gpu} and '
|
||||||
|
f'"samples_per_gpu"={cfg.data.samples_per_gpu}, "imgs_per_gpu"'
|
||||||
|
f'={cfg.data.imgs_per_gpu} is used in this experiments')
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
'Automatically set "samples_per_gpu"="imgs_per_gpu"='
|
||||||
|
f'{cfg.data.imgs_per_gpu} in this experiments')
|
||||||
|
cfg.data.samples_per_gpu = cfg.data.imgs_per_gpu
|
||||||
|
|
||||||
|
data_loaders = [
|
||||||
|
build_dataloader(
|
||||||
|
ds,
|
||||||
|
cfg.data.samples_per_gpu,
|
||||||
|
cfg.data.workers_per_gpu,
|
||||||
|
# cfg.gpus will be ignored if distributed
|
||||||
|
len(cfg.gpu_ids),
|
||||||
|
dist=distributed,
|
||||||
|
seed=cfg.seed) for ds in dataset
|
||||||
|
]
|
||||||
|
|
||||||
|
# put model on gpus
|
||||||
|
if distributed:
|
||||||
|
find_unused_parameters = cfg.get('find_unused_parameters', False)
|
||||||
|
# Sets the `find_unused_parameters` parameter in
|
||||||
|
# torch.nn.parallel.DistributedDataParallel
|
||||||
|
model = MMDistributedDataParallel(
|
||||||
|
model.cuda(),
|
||||||
|
device_ids=[torch.cuda.current_device()],
|
||||||
|
broadcast_buffers=False,
|
||||||
|
find_unused_parameters=find_unused_parameters)
|
||||||
|
else:
|
||||||
|
model = MMDataParallel(
|
||||||
|
model.cuda(cfg.gpu_ids[0]), device_ids=cfg.gpu_ids)
|
||||||
|
|
||||||
|
# build runner
|
||||||
|
optimizer = build_optimizer(model, cfg.optimizer)
|
||||||
|
|
||||||
|
if 'runner' not in cfg:
|
||||||
|
cfg.runner = {
|
||||||
|
'type': 'EpochBasedRunner',
|
||||||
|
'max_epochs': cfg.total_epochs
|
||||||
|
}
|
||||||
|
warnings.warn(
|
||||||
|
'config is now expected to have a `runner` section, '
|
||||||
|
'please set `runner` in your config.', UserWarning)
|
||||||
|
else:
|
||||||
|
if 'total_epochs' in cfg:
|
||||||
|
assert cfg.total_epochs == cfg.runner.max_epochs
|
||||||
|
|
||||||
|
runner = build_runner(
|
||||||
|
cfg.runner,
|
||||||
|
default_args=dict(
|
||||||
|
model=model,
|
||||||
|
optimizer=optimizer,
|
||||||
|
work_dir=cfg.work_dir,
|
||||||
|
logger=logger,
|
||||||
|
meta=meta))
|
||||||
|
|
||||||
|
# an ugly workaround to make .log and .log.json filenames the same
|
||||||
|
runner.timestamp = timestamp
|
||||||
|
|
||||||
|
# fp16 setting
|
||||||
|
fp16_cfg = cfg.get('fp16', None)
|
||||||
|
if fp16_cfg is not None:
|
||||||
|
optimizer_config = Fp16OptimizerHook(
|
||||||
|
**cfg.optimizer_config, **fp16_cfg, distributed=distributed)
|
||||||
|
elif distributed and 'type' not in cfg.optimizer_config:
|
||||||
|
optimizer_config = OptimizerHook(**cfg.optimizer_config)
|
||||||
|
else:
|
||||||
|
optimizer_config = cfg.optimizer_config
|
||||||
|
|
||||||
|
# register hooks
|
||||||
|
runner.register_training_hooks(cfg.lr_config, optimizer_config,
|
||||||
|
cfg.checkpoint_config, cfg.log_config,
|
||||||
|
cfg.get('momentum_config', None))
|
||||||
|
if distributed:
|
||||||
|
if isinstance(runner, EpochBasedRunner):
|
||||||
|
runner.register_hook(DistSamplerSeedHook())
|
||||||
|
|
||||||
|
# register eval hooks
|
||||||
|
if validate:
|
||||||
|
# Support batch_size > 1 in validation
|
||||||
|
val_samples_per_gpu = cfg.data.val.pop('samples_per_gpu', 1)
|
||||||
|
if val_samples_per_gpu > 1:
|
||||||
|
# Replace 'ImageToTensor' to 'DefaultFormatBundle'
|
||||||
|
cfg.data.val.pipeline = replace_ImageToTensor(
|
||||||
|
cfg.data.val.pipeline)
|
||||||
|
val_dataset = build_dataset(cfg.data.val, dict(test_mode=True))
|
||||||
|
val_dataloader = build_dataloader(
|
||||||
|
val_dataset,
|
||||||
|
samples_per_gpu=val_samples_per_gpu,
|
||||||
|
workers_per_gpu=cfg.data.workers_per_gpu,
|
||||||
|
dist=distributed,
|
||||||
|
shuffle=False)
|
||||||
|
eval_cfg = cfg.get('evaluation', {})
|
||||||
|
eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner'
|
||||||
|
eval_hook = DistEvalHook if distributed else EvalHook
|
||||||
|
runner.register_hook(eval_hook(val_dataloader, **eval_cfg))
|
||||||
|
|
||||||
|
# user-defined hooks
|
||||||
|
if cfg.get('custom_hooks', None):
|
||||||
|
custom_hooks = cfg.custom_hooks
|
||||||
|
assert isinstance(custom_hooks, list), \
|
||||||
|
f'custom_hooks expect list type, but got {type(custom_hooks)}'
|
||||||
|
for hook_cfg in cfg.custom_hooks:
|
||||||
|
assert isinstance(hook_cfg, dict), \
|
||||||
|
'Each item in custom_hooks expects dict type, but got ' \
|
||||||
|
f'{type(hook_cfg)}'
|
||||||
|
hook_cfg = hook_cfg.copy()
|
||||||
|
priority = hook_cfg.pop('priority', 'NORMAL')
|
||||||
|
hook = build_from_cfg(hook_cfg, HOOKS)
|
||||||
|
runner.register_hook(hook, priority=priority)
|
||||||
|
|
||||||
|
if cfg.resume_from:
|
||||||
|
runner.resume(cfg.resume_from)
|
||||||
|
elif cfg.load_from:
|
||||||
|
runner.load_checkpoint(cfg.load_from)
|
||||||
|
runner.run(data_loaders, cfg.workflow)
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .base_meta_learning_dataset import BaseMetaLearingDataset
|
||||||
|
|
||||||
|
__all__ = ['BaseMetaLearingDataset']
|
|
@ -0,0 +1,8 @@
|
||||||
|
# jsut an example
|
||||||
|
from mmdet.datasets.builder import DATASETS
|
||||||
|
from mmdet.datasets.custom import CustomDataset
|
||||||
|
|
||||||
|
|
||||||
|
@DATASETS.register_module()
|
||||||
|
class BaseMetaLearingDataset(CustomDataset):
|
||||||
|
pass
|
|
@ -0,0 +1 @@
|
||||||
|
from .detectors import * # noqa: F401,F403
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .base_meta_learning_detector import TestDetection
|
||||||
|
|
||||||
|
__all__ = ['TestDetection']
|
|
@ -0,0 +1,12 @@
|
||||||
|
from mmdet.models.builder import DETECTORS
|
||||||
|
from mmdet.models.detectors import BaseDetector, FasterRCNN
|
||||||
|
|
||||||
|
|
||||||
|
@DETECTORS.register_module()
|
||||||
|
class BaseMetaLearingDetector(BaseDetector):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@DETECTORS.register_module()
|
||||||
|
class TestDetection(FasterRCNN):
|
||||||
|
pass
|
|
@ -0,0 +1,10 @@
|
||||||
|
def check_config(cfg):
|
||||||
|
"""Check for missing or deprecated arguments."""
|
||||||
|
support_tasks = ['mmcls', 'mmdet']
|
||||||
|
if 'task_type' not in cfg:
|
||||||
|
raise AttributeError(f'Please set `task_type` '
|
||||||
|
f'in your config, {support_tasks} are supported')
|
||||||
|
if cfg.task_type not in support_tasks:
|
||||||
|
raise ValueError(f'{support_tasks} are supported, '
|
||||||
|
f'but get `task_type` {cfg.task_type}')
|
||||||
|
return cfg
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Copyright (c) Open-MMLab. All rights reserved.
|
||||||
|
|
||||||
|
__version__ = '0.1.0'
|
||||||
|
short_version = __version__
|
||||||
|
|
||||||
|
|
||||||
|
def parse_version_info(version_str):
|
||||||
|
version_info = []
|
||||||
|
for x in version_str.split('.'):
|
||||||
|
if x.isdigit():
|
||||||
|
version_info.append(int(x))
|
||||||
|
elif x.find('rc') != -1:
|
||||||
|
patch_version = x.split('rc')
|
||||||
|
version_info.append(int(patch_version[0]))
|
||||||
|
version_info.append(f'rc{patch_version[1]}')
|
||||||
|
return tuple(version_info)
|
||||||
|
|
||||||
|
|
||||||
|
version_info = parse_version_info(__version__)
|
|
@ -0,0 +1,7 @@
|
||||||
|
[pytest]
|
||||||
|
addopts = --xdoctest --xdoctest-style=auto
|
||||||
|
norecursedirs = .git ignore build __pycache__ data docker docs .eggs
|
||||||
|
|
||||||
|
filterwarnings= default
|
||||||
|
ignore:.*No cfgstr given in Cacher constructor or call.*:Warning
|
||||||
|
ignore:.*Define the __nice__ method for.*:Warning
|
|
@ -0,0 +1,3 @@
|
||||||
|
# These must be installed before building mmdetection
|
||||||
|
cython
|
||||||
|
numpy
|
|
@ -0,0 +1,4 @@
|
||||||
|
recommonmark
|
||||||
|
sphinx
|
||||||
|
sphinx_markdown_tables
|
||||||
|
sphinx_rtd_theme
|
|
@ -0,0 +1,5 @@
|
||||||
|
albumentations>=0.3.2
|
||||||
|
cityscapesscripts
|
||||||
|
imagecorruptions
|
||||||
|
scipy
|
||||||
|
sklearn
|
|
@ -0,0 +1,4 @@
|
||||||
|
mmcv
|
||||||
|
mmdet
|
||||||
|
torch
|
||||||
|
torchvision
|
|
@ -0,0 +1,6 @@
|
||||||
|
matplotlib
|
||||||
|
numpy
|
||||||
|
pycocotools; platform_system == "Linux"
|
||||||
|
pycocotools-windows; platform_system == "Windows"
|
||||||
|
six
|
||||||
|
terminaltables
|
|
@ -0,0 +1,11 @@
|
||||||
|
asynctest
|
||||||
|
codecov
|
||||||
|
flake8
|
||||||
|
interrogate
|
||||||
|
isort==4.3.21
|
||||||
|
# Note: used for kwarray.group_items, this may be ported to mmcv in the future.
|
||||||
|
kwarray
|
||||||
|
pytest
|
||||||
|
ubelt
|
||||||
|
xdoctest>=0.10.0
|
||||||
|
yapf
|
|
@ -0,0 +1,13 @@
|
||||||
|
[isort]
|
||||||
|
line_length = 79
|
||||||
|
multi_line_output = 0
|
||||||
|
known_standard_library = setuptools
|
||||||
|
known_first_party = mmfewshot
|
||||||
|
known_third_party = mmcls,mmcv,mmdet,numpy,torch
|
||||||
|
no_lines_before = STDLIB,LOCALFOLDER
|
||||||
|
default_section = THIRDPARTY
|
||||||
|
|
||||||
|
[yapf]
|
||||||
|
BASED_ON_STYLE = pep8
|
||||||
|
BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF = true
|
||||||
|
SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN = true
|
|
@ -0,0 +1,161 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
import torch
|
||||||
|
from torch.utils.cpp_extension import (BuildExtension, CppExtension,
|
||||||
|
CUDAExtension)
|
||||||
|
|
||||||
|
|
||||||
|
def readme():
|
||||||
|
with open('README.md', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
version_file = 'mmdet/version.py'
|
||||||
|
|
||||||
|
|
||||||
|
def get_version():
|
||||||
|
with open(version_file, 'r') as f:
|
||||||
|
exec(compile(f.read(), version_file, 'exec'))
|
||||||
|
return locals()['__version__']
|
||||||
|
|
||||||
|
|
||||||
|
def make_cuda_ext(name, module, sources, sources_cuda=[]):
|
||||||
|
|
||||||
|
define_macros = []
|
||||||
|
extra_compile_args = {'cxx': []}
|
||||||
|
|
||||||
|
if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1':
|
||||||
|
define_macros += [('WITH_CUDA', None)]
|
||||||
|
extension = CUDAExtension
|
||||||
|
extra_compile_args['nvcc'] = [
|
||||||
|
'-D__CUDA_NO_HALF_OPERATORS__',
|
||||||
|
'-D__CUDA_NO_HALF_CONVERSIONS__',
|
||||||
|
'-D__CUDA_NO_HALF2_OPERATORS__',
|
||||||
|
]
|
||||||
|
sources += sources_cuda
|
||||||
|
else:
|
||||||
|
print(f'Compiling {name} without CUDA')
|
||||||
|
extension = CppExtension
|
||||||
|
|
||||||
|
return extension(
|
||||||
|
name=f'{module}.{name}',
|
||||||
|
sources=[os.path.join(*module.split('.'), p) for p in sources],
|
||||||
|
define_macros=define_macros,
|
||||||
|
extra_compile_args=extra_compile_args)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_requirements(fname='requirements.txt', with_version=True):
|
||||||
|
"""Parse the package dependencies listed in a requirements file but strips
|
||||||
|
specific versioning information.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fname (str): path to requirements file
|
||||||
|
with_version (bool, default=False): if True include version specs
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[str]: list of requirements items
|
||||||
|
|
||||||
|
CommandLine:
|
||||||
|
python -c "import setup; print(setup.parse_requirements())"
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
from os.path import exists
|
||||||
|
import re
|
||||||
|
require_fpath = fname
|
||||||
|
|
||||||
|
def parse_line(line):
|
||||||
|
"""Parse information from a line in a requirements text file."""
|
||||||
|
if line.startswith('-r '):
|
||||||
|
# Allow specifying requirements in other files
|
||||||
|
target = line.split(' ')[1]
|
||||||
|
for info in parse_require_file(target):
|
||||||
|
yield info
|
||||||
|
else:
|
||||||
|
info = {'line': line}
|
||||||
|
if line.startswith('-e '):
|
||||||
|
info['package'] = line.split('#egg=')[1]
|
||||||
|
elif '@git+' in line:
|
||||||
|
info['package'] = line
|
||||||
|
else:
|
||||||
|
# Remove versioning from the package
|
||||||
|
pat = '(' + '|'.join(['>=', '==', '>']) + ')'
|
||||||
|
parts = re.split(pat, line, maxsplit=1)
|
||||||
|
parts = [p.strip() for p in parts]
|
||||||
|
|
||||||
|
info['package'] = parts[0]
|
||||||
|
if len(parts) > 1:
|
||||||
|
op, rest = parts[1:]
|
||||||
|
if ';' in rest:
|
||||||
|
# Handle platform specific dependencies
|
||||||
|
# http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies
|
||||||
|
version, platform_deps = map(str.strip,
|
||||||
|
rest.split(';'))
|
||||||
|
info['platform_deps'] = platform_deps
|
||||||
|
else:
|
||||||
|
version = rest # NOQA
|
||||||
|
info['version'] = (op, version)
|
||||||
|
yield info
|
||||||
|
|
||||||
|
def parse_require_file(fpath):
|
||||||
|
with open(fpath, 'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
line = line.strip()
|
||||||
|
if line and not line.startswith('#'):
|
||||||
|
for info in parse_line(line):
|
||||||
|
yield info
|
||||||
|
|
||||||
|
def gen_packages_items():
|
||||||
|
if exists(require_fpath):
|
||||||
|
for info in parse_require_file(require_fpath):
|
||||||
|
parts = [info['package']]
|
||||||
|
if with_version and 'version' in info:
|
||||||
|
parts.extend(info['version'])
|
||||||
|
if not sys.version.startswith('3.4'):
|
||||||
|
# apparently package_deps are broken in 3.4
|
||||||
|
platform_deps = info.get('platform_deps')
|
||||||
|
if platform_deps is not None:
|
||||||
|
parts.append(';' + platform_deps)
|
||||||
|
item = ''.join(parts)
|
||||||
|
yield item
|
||||||
|
|
||||||
|
packages = list(gen_packages_items())
|
||||||
|
return packages
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setup(
|
||||||
|
name='mmdet',
|
||||||
|
version=get_version(),
|
||||||
|
description='OpenMMLab Detection Toolbox and Benchmark',
|
||||||
|
long_description=readme(),
|
||||||
|
long_description_content_type='text/markdown',
|
||||||
|
author='OpenMMLab',
|
||||||
|
author_email='openmmlab@gmail.com',
|
||||||
|
keywords='computer vision, object detection',
|
||||||
|
url='https://github.com/open-mmlab/mmdetection',
|
||||||
|
packages=find_packages(exclude=('configs', 'tools', 'demo')),
|
||||||
|
classifiers=[
|
||||||
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
'License :: OSI Approved :: Apache Software License',
|
||||||
|
'Operating System :: OS Independent',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.6',
|
||||||
|
'Programming Language :: Python :: 3.7',
|
||||||
|
'Programming Language :: Python :: 3.8',
|
||||||
|
],
|
||||||
|
license='Apache License 2.0',
|
||||||
|
setup_requires=parse_requirements('requirements/build.txt'),
|
||||||
|
tests_require=parse_requirements('requirements/tests.txt'),
|
||||||
|
install_requires=parse_requirements('requirements/runtime.txt'),
|
||||||
|
extras_require={
|
||||||
|
'all': parse_requirements('requirements.txt'),
|
||||||
|
'tests': parse_requirements('requirements/tests.txt'),
|
||||||
|
'build': parse_requirements('requirements/build.txt'),
|
||||||
|
'optional': parse_requirements('requirements/optional.txt'),
|
||||||
|
},
|
||||||
|
ext_modules=[],
|
||||||
|
cmdclass={'build_ext': BuildExtension},
|
||||||
|
zip_safe=False)
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
CONFIG=$1
|
||||||
|
CHECKPOINT=$2
|
||||||
|
GPUS=$3
|
||||||
|
PORT=${PORT:-29500}
|
||||||
|
|
||||||
|
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}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
CONFIG=$1
|
||||||
|
GPUS=$2
|
||||||
|
PORT=${PORT:-29500}
|
||||||
|
|
||||||
|
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
|
||||||
|
python -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=$PORT \
|
||||||
|
$(dirname "$0")/train.py $CONFIG --launcher pytorch ${@:3}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/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 \
|
||||||
|
${SRUN_ARGS} \
|
||||||
|
python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/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:-5}
|
||||||
|
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 \
|
||||||
|
${SRUN_ARGS} \
|
||||||
|
python -u tools/train.py ${CONFIG} --work-dir=${WORK_DIR} --launcher="slurm" ${PY_ARGS}
|
|
@ -0,0 +1,221 @@
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
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 mmfewshot # 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('--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(
|
||||||
|
'--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)
|
||||||
|
# import modules from string list.
|
||||||
|
if cfg.get('custom_imports', None):
|
||||||
|
from mmcv.utils import import_modules_from_strings
|
||||||
|
import_modules_from_strings(**cfg['custom_imports'])
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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'))
|
||||||
|
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=[0])
|
||||||
|
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'
|
||||||
|
]:
|
||||||
|
eval_kwargs.pop(key, None)
|
||||||
|
eval_kwargs.update(dict(metric=args.eval, **kwargs))
|
||||||
|
print(dataset.evaluate(outputs, **eval_kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,195 @@
|
||||||
|
import argparse
|
||||||
|
import copy
|
||||||
|
import os
|
||||||
|
import os.path as osp
|
||||||
|
import time
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import mmcv
|
||||||
|
import torch
|
||||||
|
from mmcv import Config, DictAction
|
||||||
|
from mmcv.runner import get_dist_info, init_dist
|
||||||
|
from mmcv.utils import get_git_hash
|
||||||
|
from mmdet.utils import collect_env, get_root_logger
|
||||||
|
|
||||||
|
import mmfewshot # noqa: F401, F403
|
||||||
|
from mmfewshot import __version__
|
||||||
|
from mmfewshot.apis import set_random_seed, train_model
|
||||||
|
from mmfewshot.builders.dataset_builder import build_dataset
|
||||||
|
from mmfewshot.builders.model_builder import build_model
|
||||||
|
from mmfewshot.utils.check_config import check_config
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
'--no-validate',
|
||||||
|
action='store_true',
|
||||||
|
help='whether not to evaluate the checkpoint during training')
|
||||||
|
group_gpus = parser.add_mutually_exclusive_group()
|
||||||
|
group_gpus.add_argument(
|
||||||
|
'--gpus',
|
||||||
|
type=int,
|
||||||
|
help='number of gpus to use '
|
||||||
|
'(only applicable to non-distributed training)')
|
||||||
|
group_gpus.add_argument(
|
||||||
|
'--gpu-ids',
|
||||||
|
type=int,
|
||||||
|
nargs='+',
|
||||||
|
help='ids of gpus to use '
|
||||||
|
'(only applicable to non-distributed training)')
|
||||||
|
parser.add_argument('--seed', type=int, default=None, help='random seed')
|
||||||
|
parser.add_argument(
|
||||||
|
'--deterministic',
|
||||||
|
action='store_true',
|
||||||
|
help='whether to set deterministic options for CUDNN backend.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--options',
|
||||||
|
nargs='+',
|
||||||
|
action=DictAction,
|
||||||
|
help='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)
|
||||||
|
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)
|
||||||
|
|
||||||
|
if args.cfg_options is not None:
|
||||||
|
cfg.merge_from_dict(args.cfg_options)
|
||||||
|
|
||||||
|
cfg = check_config(cfg)
|
||||||
|
|
||||||
|
# import modules from string list.
|
||||||
|
if cfg.get('custom_imports', None):
|
||||||
|
from mmcv.utils import import_modules_from_strings
|
||||||
|
import_modules_from_strings(**cfg['custom_imports'])
|
||||||
|
# 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
|
||||||
|
if args.gpu_ids is not None:
|
||||||
|
cfg.gpu_ids = args.gpu_ids
|
||||||
|
else:
|
||||||
|
cfg.gpu_ids = range(1) if args.gpus is None else range(args.gpus)
|
||||||
|
|
||||||
|
# init distributed env first, since logger depends on the dist info.
|
||||||
|
if args.launcher == 'none':
|
||||||
|
distributed = False
|
||||||
|
else:
|
||||||
|
distributed = True
|
||||||
|
init_dist(args.launcher, **cfg.dist_params)
|
||||||
|
# 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}')
|
||||||
|
|
||||||
|
# set random seeds
|
||||||
|
if args.seed is not None:
|
||||||
|
logger.info(f'Set random seed to {args.seed}, '
|
||||||
|
f'deterministic: {args.deterministic}')
|
||||||
|
set_random_seed(args.seed, deterministic=args.deterministic)
|
||||||
|
cfg.seed = args.seed
|
||||||
|
meta['seed'] = args.seed
|
||||||
|
meta['exp_name'] = osp.basename(args.config)
|
||||||
|
|
||||||
|
model = build_model(
|
||||||
|
cfg.model,
|
||||||
|
train_cfg=cfg.get('train_cfg'),
|
||||||
|
test_cfg=cfg.get('test_cfg'),
|
||||||
|
task_type=cfg.task_type)
|
||||||
|
model.init_weights()
|
||||||
|
|
||||||
|
datasets = [build_dataset(cfg.data.train, task_type=cfg.task_type)]
|
||||||
|
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, task_type=cfg.task_type))
|
||||||
|
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(
|
||||||
|
mmfewshot_version=__version__ + get_git_hash()[:7],
|
||||||
|
CLASSES=datasets[0].CLASSES)
|
||||||
|
# add an attribute for visualization convenience
|
||||||
|
model.CLASSES = datasets[0].CLASSES
|
||||||
|
train_model(
|
||||||
|
model,
|
||||||
|
datasets,
|
||||||
|
cfg,
|
||||||
|
distributed=distributed,
|
||||||
|
validate=(not args.no_validate),
|
||||||
|
timestamp=timestamp,
|
||||||
|
meta=meta)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Copyright (c) Open-MMLab. All rights reserved.
|
||||||
|
|
||||||
|
__version__ = '0.1.0'
|
||||||
|
short_version = __version__
|
||||||
|
|
||||||
|
|
||||||
|
def parse_version_info(version_str):
|
||||||
|
version_info = []
|
||||||
|
for x in version_str.split('.'):
|
||||||
|
if x.isdigit():
|
||||||
|
version_info.append(int(x))
|
||||||
|
elif x.find('rc') != -1:
|
||||||
|
patch_version = x.split('rc')
|
||||||
|
version_info.append(int(patch_version[0]))
|
||||||
|
version_info.append(f'rc{patch_version[1]}')
|
||||||
|
return tuple(version_info)
|
||||||
|
|
||||||
|
|
||||||
|
version_info = parse_version_info(__version__)
|
Loading…
Reference in New Issue