-
Notifications
You must be signed in to change notification settings - Fork 567
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
113 changed files
with
2,340 additions
and
1,163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
# Copyright (c) OpenMMLab. All rights reserved. | ||
import argparse | ||
import glob | ||
import json | ||
import os.path as osp | ||
import shutil | ||
import subprocess | ||
from collections import OrderedDict | ||
|
||
import mmcv | ||
import torch | ||
import yaml | ||
|
||
|
||
def ordered_yaml_dump(data, stream=None, Dumper=yaml.SafeDumper, **kwds): | ||
|
||
class OrderedDumper(Dumper): | ||
pass | ||
|
||
def _dict_representer(dumper, data): | ||
return dumper.represent_mapping( | ||
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, data.items()) | ||
|
||
OrderedDumper.add_representer(OrderedDict, _dict_representer) | ||
return yaml.dump(data, stream, OrderedDumper, **kwds) | ||
|
||
|
||
def process_checkpoint(in_file, out_file): | ||
checkpoint = torch.load(in_file, map_location='cpu') | ||
# remove optimizer for smaller file size | ||
if 'optimizer' in checkpoint: | ||
del checkpoint['optimizer'] | ||
|
||
# remove ema state_dict | ||
for key in list(checkpoint['state_dict']): | ||
if key.startswith('ema_'): | ||
checkpoint['state_dict'].pop(key) | ||
|
||
# if it is necessary to remove some sensitive data in checkpoint['meta'], | ||
# add the code here. | ||
if torch.__version__ >= '1.6': | ||
torch.save(checkpoint, out_file, _use_new_zipfile_serialization=False) | ||
else: | ||
torch.save(checkpoint, out_file) | ||
sha = subprocess.check_output(['sha256sum', out_file]).decode() | ||
final_file = out_file.rstrip('.pth') + '-{}.pth'.format(sha[:8]) | ||
subprocess.Popen(['mv', out_file, final_file]) | ||
return final_file | ||
|
||
|
||
def get_final_epoch(config): | ||
cfg = mmcv.Config.fromfile('./configs/' + config) | ||
return cfg.runner.max_epochs | ||
|
||
|
||
def get_best_epoch(exp_dir): | ||
best_epoch_full_path = list( | ||
sorted(glob.glob(osp.join(exp_dir, 'best_*.pth'))))[-1] | ||
best_epoch_model_path = best_epoch_full_path.split('/')[-1] | ||
best_epoch = best_epoch_model_path.split('_')[-1].split('.')[0] | ||
return best_epoch_model_path, int(best_epoch) | ||
|
||
|
||
def get_real_epoch(config): | ||
cfg = mmcv.Config.fromfile('./configs/' + config) | ||
epoch = cfg.runner.max_epochs | ||
if cfg.data.train.type == 'RepeatDataset': | ||
epoch *= cfg.data.train.times | ||
return epoch | ||
|
||
|
||
def get_final_results(log_json_path, epoch, results_lut): | ||
result_dict = dict() | ||
with open(log_json_path, 'r') as f: | ||
for line in f.readlines(): | ||
log_line = json.loads(line) | ||
if 'mode' not in log_line.keys(): | ||
continue | ||
|
||
if log_line['mode'] == 'train' and log_line['epoch'] == epoch: | ||
result_dict['memory'] = log_line['memory'] | ||
|
||
if log_line['mode'] == 'val' and log_line['epoch'] == epoch: | ||
result_dict.update({ | ||
key: log_line[key] | ||
for key in results_lut if key in log_line | ||
}) | ||
return result_dict | ||
|
||
|
||
def get_dataset_name(config): | ||
# If there are more dataset, add here. | ||
name_map = dict( | ||
HRSCDataset='HRSC 2016', SARDataset='SAR', DOTADataset='DOTA v1.0') | ||
cfg = mmcv.Config.fromfile('./configs/' + config) | ||
return name_map[cfg.dataset_type] | ||
|
||
|
||
def convert_model_info_to_pwc(model_infos): | ||
pwc_files = {} | ||
for model in model_infos: | ||
cfg_folder_name = osp.split(model['config'])[-2] | ||
pwc_model_info = OrderedDict() | ||
pwc_model_info['Name'] = osp.split(model['config'])[-1].split('.')[0] | ||
pwc_model_info['In Collection'] = 'Please fill in Collection name' | ||
pwc_model_info['Config'] = osp.join('configs', model['config']) | ||
|
||
# get metadata | ||
memory = round(model['results']['memory'] / 1024, 1) | ||
epochs = get_real_epoch(model['config']) | ||
meta_data = OrderedDict() | ||
meta_data['Training Memory (GB)'] = memory | ||
meta_data['Epochs'] = epochs | ||
pwc_model_info['Metadata'] = meta_data | ||
|
||
# get dataset name | ||
dataset_name = get_dataset_name(model['config']) | ||
|
||
# get results | ||
results = [] | ||
# if there are more metrics, add here. | ||
if 'mAP' in model['results']: | ||
metric = round(model['results']['mAP'] * 100, 1) | ||
results.append( | ||
OrderedDict( | ||
Task='Object Detection', | ||
Dataset=dataset_name, | ||
Metrics={'box AP': metric})) | ||
pwc_model_info['Results'] = results | ||
|
||
link_string = 'https://download.openmmlab.com/mmrotate/v0.1.0/' | ||
link_string += '{}/{}'.format(model['config'].rstrip('.py'), | ||
osp.split(model['model_path'])[-1]) | ||
pwc_model_info['Weights'] = link_string | ||
if cfg_folder_name in pwc_files: | ||
pwc_files[cfg_folder_name].append(pwc_model_info) | ||
else: | ||
pwc_files[cfg_folder_name] = [pwc_model_info] | ||
return pwc_files | ||
|
||
|
||
def parse_args(): | ||
parser = argparse.ArgumentParser(description='Gather benchmarked models') | ||
parser.add_argument( | ||
'root', | ||
type=str, | ||
help='root path of benchmarked models to be gathered') | ||
parser.add_argument( | ||
'out', type=str, help='output path of gathered models to be stored') | ||
parser.add_argument( | ||
'--best', | ||
action='store_true', | ||
help='whether to gather the best model.') | ||
|
||
args = parser.parse_args() | ||
return args | ||
|
||
|
||
def main(): | ||
args = parse_args() | ||
models_root = args.root | ||
models_out = args.out | ||
mmcv.mkdir_or_exist(models_out) | ||
|
||
# find all models in the root directory to be gathered | ||
raw_configs = list(mmcv.scandir('./configs', '.py', recursive=True)) | ||
|
||
# filter configs that is not trained in the experiments dir | ||
used_configs = [] | ||
for raw_config in raw_configs: | ||
if osp.exists(osp.join(models_root, raw_config)): | ||
used_configs.append(raw_config) | ||
print(f'Find {len(used_configs)} models to be gathered') | ||
|
||
# find final_ckpt and log file for trained each config | ||
# and parse the best performance | ||
model_infos = [] | ||
for used_config in used_configs: | ||
exp_dir = osp.join(models_root, used_config) | ||
# check whether the exps is finished | ||
if args.best is True: | ||
final_model, final_epoch = get_best_epoch(exp_dir) | ||
else: | ||
final_epoch = get_final_epoch(used_config) | ||
final_model = 'epoch_{}.pth'.format(final_epoch) | ||
|
||
model_path = osp.join(exp_dir, final_model) | ||
# skip if the model is still training | ||
if not osp.exists(model_path): | ||
continue | ||
|
||
# get the latest logs | ||
log_json_path = list( | ||
sorted(glob.glob(osp.join(exp_dir, '*.log.json'))))[-1] | ||
log_txt_path = list(sorted(glob.glob(osp.join(exp_dir, '*.log'))))[-1] | ||
cfg = mmcv.Config.fromfile('./configs/' + used_config) | ||
results_lut = cfg.evaluation.metric | ||
if not isinstance(results_lut, list): | ||
results_lut = [results_lut] | ||
model_performance = get_final_results(log_json_path, final_epoch, | ||
results_lut) | ||
|
||
if model_performance is None: | ||
continue | ||
|
||
model_time = osp.split(log_txt_path)[-1].split('.')[0] | ||
model_infos.append( | ||
dict( | ||
config=used_config, | ||
results=model_performance, | ||
epochs=final_epoch, | ||
model_time=model_time, | ||
final_model=final_model, | ||
log_json_path=osp.split(log_json_path)[-1])) | ||
|
||
# publish model for each checkpoint | ||
publish_model_infos = [] | ||
for model in model_infos: | ||
model_publish_dir = osp.join(models_out, model['config'].rstrip('.py')) | ||
mmcv.mkdir_or_exist(model_publish_dir) | ||
|
||
model_name = osp.split(model['config'])[-1].split('.')[0] | ||
|
||
model_name += '_' + model['model_time'] | ||
publish_model_path = osp.join(model_publish_dir, model_name) | ||
trained_model_path = osp.join(models_root, model['config'], | ||
model['final_model']) | ||
|
||
# convert model | ||
final_model_path = process_checkpoint(trained_model_path, | ||
publish_model_path) | ||
|
||
# copy log | ||
shutil.copy( | ||
osp.join(models_root, model['config'], model['log_json_path']), | ||
osp.join(model_publish_dir, f'{model_name}.log.json')) | ||
shutil.copy( | ||
osp.join(models_root, model['config'], | ||
model['log_json_path'].rstrip('.json')), | ||
osp.join(model_publish_dir, f'{model_name}.log')) | ||
|
||
# copy config to guarantee reproducibility | ||
config_path = model['config'] | ||
config_path = osp.join( | ||
'configs', | ||
config_path) if 'configs' not in config_path else config_path | ||
target_config_path = osp.split(config_path)[-1] | ||
shutil.copy(config_path, osp.join(model_publish_dir, | ||
target_config_path)) | ||
|
||
model['model_path'] = final_model_path | ||
publish_model_infos.append(model) | ||
|
||
models = dict(models=publish_model_infos) | ||
print(f'Totally gathered {len(publish_model_infos)} models') | ||
mmcv.dump(models, osp.join(models_out, 'model_info.json')) | ||
|
||
pwc_files = convert_model_info_to_pwc(publish_model_infos) | ||
for name in pwc_files: | ||
with open(osp.join(models_out, name + '_metafile.yml'), 'w') as f: | ||
ordered_yaml_dump(pwc_files[name], f, encoding='utf-8') | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# CSL | ||
> [Arbitrary-Oriented Object Detection with Circular Smooth Label](https://link.springer.com/chapter/10.1007/978-3-030-58598-3_40) | ||
<!-- [ALGORITHM] --> | ||
## Abstract | ||
|
||
<div align=center> | ||
<img src="https://raw.githubusercontent.com/zytx121/image-host/main/imgs/csl.jpg" width="800"/> | ||
</div> | ||
|
||
Arbitrary-oriented object detection has recently attracted increasing attention in vision for their importance | ||
in aerial imagery, scene text, and face etc. In this paper, we show that existing regression-based rotation detectors | ||
suffer the problem of discontinuous boundaries, which is directly caused by angular periodicity or corner ordering. | ||
By a careful study, we find the root cause is that the ideal predictions are beyond the defined range. We design a | ||
new rotation detection baseline, to address the boundary problem by transforming angular prediction from a regression | ||
problem to a classification task with little accuracy loss, whereby high-precision angle classification is devised in | ||
contrast to previous works using coarse-granularity in rotation detection. We also propose a circular smooth label (CSL) | ||
technique to handle the periodicity of the angle and increase the error tolerance to adjacent angles. We further | ||
introduce four window functions in CSL and explore the effect of different window radius sizes on detection performance. | ||
Extensive experiments and visual analysis on two large-scale public datasets for aerial images i.e. DOTA, HRSC2016, | ||
as well as scene text dataset ICDAR2015 and MLT, show the effectiveness of our approach. | ||
|
||
## Results and models | ||
|
||
DOTA1.0 | ||
|
||
| Backbone | mAP | Angle | Window func. | Omega | lr schd | Mem (GB) | Inf Time (fps) | Aug | Batch Size | Configs | Download | | ||
|:------------:|:----------:|:-----------:|:-----------:|:-----------:|:---------:|:---------:|:---------:|:---------:|:---------:|:---------:|:-------------:| | ||
| ResNet50 (1024,1024,200) | 68.42 | le90 | - | - | 1x | 3.38 | 17.8 | - | 2 | [rotated_retinanet_obb_r50_fpn_1x_dota_le90](./rotated_retinanet_obb_r50_fpn_1x_dota_le90.py) | [model](https://download.openmmlab.com/mmrotate/v0.1.0/rotated_retinanet/rotated_retinanet_obb_r50_fpn_1x_dota_le90/rotated_retinanet_obb_r50_fpn_1x_dota_le90-c0097bc4.pth) | [log](https://download.openmmlab.com/mmrotate/v0.1.0/rotated_retinanet/rotated_retinanet_obb_r50_fpn_1x_dota_le90/rotated_retinanet_obb_r50_fpn_1x_dota_le90_20220128_130740.log.json) | ||
| ResNet50 (1024,1024,200) | 68.79 | le90 | - | - | 1x | 2.36 | 25.9 | - | 2 | [rotated_retinanet_obb_r50_fpn_fp16_1x_dota_le90](./rotated_retinanet_obb_r50_fpn_fp16_1x_dota_le90.py) | [model](https://download.openmmlab.com/mmrotate/v0.1.0/rotated_retinanet/rotated_retinanet_obb_r50_fpn_fp16_1x_dota_le90/rotated_retinanet_obb_r50_fpn_fp16_1x_dota_le90-01de71b5.pth) | [log](https://download.openmmlab.com/mmrotate/v0.1.0/rotated_retinanet/rotated_retinanet_obb_r50_fpn_fp16_1x_dota_le90/rotated_retinanet_obb_r50_fpn_fp16_1x_dota_le90_20220303_183714.log.json) | ||
| ResNet50 (1024,1024,200) | 69.51 | le90 | Gaussian | 4 | 1x | 2.60 | 24.0 | - | 2 | [rotated_retinanet_obb_csl_gaussian_r50_fpn_fp16_1x_dota_le90](./rotated_retinanet_obb_csl_gaussian_r50_fpn_fp16_1x_dota_le90.py) | [model](https://download.openmmlab.com/mmrotate/v0.1.0/csl/rotated_retinanet_obb_csl_gaussian_r50_fpn_fp16_1x_dota_le90/rotated_retinanet_obb_csl_gaussian_r50_fpn_fp16_1x_dota_le90-b4271aed.pth) | [log](https://download.openmmlab.com/mmrotate/v0.1.0/csl/rotated_retinanet_obb_csl_gaussian_r50_fpn_fp16_1x_dota_le90/rotated_retinanet_obb_csl_gaussian_r50_fpn_fp16_1x_dota_le90_20220321_010033.log.json) | ||
|
||
|
||
## Citation | ||
``` | ||
@inproceedings{yang2020arbitrary, | ||
title={Arbitrary-Oriented Object Detection with Circular Smooth Label}, | ||
author={Yang, Xue and Yan, Junchi}, | ||
booktitle={European Conference on Computer Vision}, | ||
pages={677--694}, | ||
year={2020} | ||
} | ||
``` |
Oops, something went wrong.