├── README.md ├── assets ├── Hall.png ├── OFVL_overall.png ├── Parking_lot1.png ├── Parking_lot2.png ├── Room.png ├── laser_image.png └── overall.png ├── configs ├── _base_ │ └── default_runtime.py ├── ofvl_ms │ └── README.md └── ufvl_net │ ├── 12scenes.py │ ├── 7scenes.py │ └── README.md ├── model-index.yml ├── requirements ├── docs.txt ├── mminstall.txt ├── optional.txt ├── readthedocs.txt ├── runtime.txt └── tests.txt ├── setup.py ├── tools ├── integrate_params.py ├── slurm_test.sh └── test.py ├── ufvl_net.egg-info ├── PKG-INFO ├── SOURCES.txt ├── dependency_links.txt ├── not-zip-safe ├── requires.txt └── top_level.txt └── ufvl_net ├── .mim ├── configs ├── model-index.yml └── tools ├── __init__.py ├── __pycache__ ├── __init__.cpython-37.pyc └── version.cpython-37.pyc ├── apis ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── inference.cpython-37.pyc │ ├── test.cpython-37.pyc │ └── train.cpython-37.pyc ├── inference.py ├── test.py └── train.py ├── cnn ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── __init__.cpython-38.pyc │ ├── conv2d_share.cpython-37.pyc │ └── conv2d_share.cpython-38.pyc ├── cnn │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── __init__.cpython-38.pyc │ │ ├── conv2d_share.cpython-37.pyc │ │ └── conv2d_share.cpython-38.pyc │ └── conv2d_share.py └── conv2d_share.py ├── datasets ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── base_dataset.cpython-37.pyc │ ├── builder.cpython-37.pyc │ ├── cambridge.cpython-37.pyc │ ├── cifar.cpython-37.pyc │ ├── cub.cpython-37.pyc │ ├── custom.cpython-37.pyc │ ├── dataset_wrappers.cpython-37.pyc │ ├── imagenet.cpython-37.pyc │ ├── imagenet21k.cpython-37.pyc │ ├── mnist.cpython-37.pyc │ ├── multi_label.cpython-37.pyc │ ├── seven_scenes.cpython-37.pyc │ ├── twelve_scenes.cpython-37.pyc │ ├── twelvescenes.cpython-37.pyc │ ├── utils.cpython-37.pyc │ └── voc.cpython-37.pyc ├── builder.py ├── dataset_wrappers.py ├── piplines │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── auto_augment.cpython-37.pyc │ │ ├── compose.cpython-37.pyc │ │ ├── formatting.cpython-37.pyc │ │ ├── loading.cpython-37.pyc │ │ └── transforms.cpython-37.pyc │ ├── auto_augment.py │ ├── compose.py │ ├── formatting.py │ ├── loading.py │ └── transforms.py ├── seven_scenes.py └── twelvescenes.py ├── models ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ └── builder.cpython-37.pyc ├── architecture │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── base.cpython-37.pyc │ │ └── vis_loc.cpython-37.pyc │ ├── base.py │ ├── pnpransac │ │ ├── build │ │ │ └── temp.linux-x86_64-cpython-37 │ │ │ │ └── pnpransacpy.o │ │ ├── pnpransac.cpp │ │ ├── pnpransac.cpython-37m-x86_64-linux-gnu.so │ │ ├── pnpransac.h │ │ ├── pnpransacpy.cpp │ │ ├── pnpransacpy.pxd │ │ ├── pnpransacpy.pyx │ │ └── setup.py │ └── vis_loc.py ├── backbones │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── base_backbone.cpython-37.pyc │ │ ├── resnet.cpython-37.pyc │ │ └── seresnet.cpython-37.pyc │ ├── base_backbone.py │ ├── resnet.py │ └── seresnet.py ├── builder.py ├── heads │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ └── score_head.cpython-37.pyc │ └── score_head.py └── utils │ ├── __pycache__ │ ├── make_divisible.cpython-37.pyc │ └── se_layer.cpython-37.pyc │ ├── augment │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── augments.cpython-37.pyc │ │ ├── builder.cpython-37.pyc │ │ ├── cutmix.cpython-37.pyc │ │ ├── identity.cpython-37.pyc │ │ ├── mixup.cpython-37.pyc │ │ ├── resizemix.cpython-37.pyc │ │ └── utils.cpython-37.pyc │ ├── augments.py │ ├── builder.py │ ├── cutmix.py │ ├── identity.py │ ├── mixup.py │ ├── resizemix.py │ └── utils.py │ ├── make_divisible.py │ └── se_layer.py ├── utils ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── collect_env.cpython-37.pyc │ ├── logger.cpython-37.pyc │ └── setup_env.cpython-37.pyc ├── collect_env.py ├── logger.py └── setup_env.py └── version.py /README.md: -------------------------------------------------------------------------------- 1 | # Visual localization with SCoRE methods 2 | *** This is a collection of our visual localization frameworks. 3 | 4 | > [**OFVL-MS**](./configs/ofvl_ms) (```@ICCV'23```): **OFVL-MS: Once for Visual Localization across Multiple Indoor Scenes** 5 | 6 | > [**UFVL-Net**](./configs/ufvl_net) (```@TIM'23```): **UFVL-Net: A Unified Framework for Visual Localization across Multiple Indoor Scenesss** 7 | 8 | ## Highlights 9 | - Once-for-multiple-scenes. 10 | Both OFVL-MS and UFVL-Net optimize visual localization tasks of various scenes collectively using a multi-task learning manner, which challenges the conventional wisdom that SCoRe typically trains a separate model for each scene. OFVL-MS realizes layer-wise parameters sharing, while UFVL-Net realizes channel-wise and kernel-wise sharing polices. 11 | 12 | - Competive performance. 13 | Both OFVL-MS and UFVL-Net deliver extraordinary performances on two benchmarks and complex real scenes. We demonstrate that once the training for our methods are done, our methods can generalize to new scenes with much fewer parameters by freezing the task-shared parameters. 14 | 15 | ## Environment Setup 16 | To set up the enviroment you can easily run the following command: 17 | - Create environment 18 | ```buildoutcfg 19 | conda create -n ufvlnet python=3.7 20 | conda activate ufvlnet 21 | ``` 22 | - Install torch, we verify UFVL-Net with pytorch 1.10.1 and cuda 11.3. 23 | ```buildoutcfg 24 | conda install pytorch==1.10.1 torchvision==0.11.2 torchaudio==0.10.1 cudatoolkit=11.3 -c pytorch -c conda-forge 25 | ``` 26 | - Subsequently, we need to build the cython module to install the PnP solver: 27 | ```buildoutcfg 28 | cd ./pnpransac 29 | rm -rf build 30 | python setup.py build_ext --inplace 31 | ``` 32 | - Install openmmlab packages. 33 | ```buildoutcfg 34 | pip install mmcv-full==1.5.0 35 | ``` 36 | - Compile the ufvl-net as a package. 37 | ```buildoutcfg 38 | cd ufvl_net 39 | pip install -e . 40 | cd .. 41 | export PYTHONPATH=./ufvl_net/ 42 | ``` 43 | ## Data Preparation 44 | We utilize two standard datasets (i.e, 7-Scenes and 12-Scenes) to evaluate our method. 45 | - 7-Scenes: The 7-Scenes dataset can be downloaded from [7-Scenes](https://www.microsoft.com/en-us/research/project/rgb-d-dataset-7-scenes/). 46 | - 12-Scenes: The 12-Scenes dataset can be downloaded from [12-Scenes](https://graphics.stanford.edu/projects/reloc/). 47 | - LIVL: The real-world LIVL dataset can be downloaded from [RealWorld-Scenes](https://drive.google.com/drive/folders/1rHILFijnb8wfQiT-5gWLvDJWbOqHMZwx). 48 | 49 | ## LIVL Dataset 50 | LIVL dataset collection equipment contains a mobile chassis, a RealSense D435 camera, and a VLP-16 laser radar. LIVL dataset records RGB-D images and corresponding camera poses of four different indoor environments. 51 | The dataset is available at [here](https://drive.google.com/drive/folders/1rHILFijnb8wfQiT-5gWLvDJWbOqHMZwx). 52 | Specifically, we utilize the ROS system to record RGB images and aligned depth images with corresponding timestamp $T_{1}$, Furthermore, we obtain point clouds with timestamp $T_{2}$ provided by VLP-16 laser radar. Then, we generate final RGB-D images and corresponding point clouds through aligning $T_{1}$ and $T_{2}$. Ultimately, We utilize the LiDAR-based SLAM system A-LOAM to compute the ground truth pose. 53 | For each scene, four sequences are recorded, in which three sequences are used for training and one sequence for testing. 54 | 55 | ![](https://github.com/mooncake199809/UFVL-Net/blob/main/assets/laser_image.png) 56 | - K544: a room spanning about $12 \times 9 m^{2}$ with $3109$ images for training and $1112$ images for testing. 57 | - Floor5: a hall spanning about $12 \times 5 m^{2}$ with $2694$ images for training and $869$ images for testing. 58 | - Parking lot1: a parking lot spanning about $8 \times 6 m^{2}$ with $2294$ images for training and $661$ images for testing. 59 | - Parking lot2: a parking lot spanning about $8 \times 8 m^{2}$ with $2415$ images for training and $875$ images for testing. 60 | -------------------------------------------------------------------------------- /assets/Hall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/assets/Hall.png -------------------------------------------------------------------------------- /assets/OFVL_overall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/assets/OFVL_overall.png -------------------------------------------------------------------------------- /assets/Parking_lot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/assets/Parking_lot1.png -------------------------------------------------------------------------------- /assets/Parking_lot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/assets/Parking_lot2.png -------------------------------------------------------------------------------- /assets/Room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/assets/Room.png -------------------------------------------------------------------------------- /assets/laser_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/assets/laser_image.png -------------------------------------------------------------------------------- /assets/overall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/assets/overall.png -------------------------------------------------------------------------------- /configs/_base_/default_runtime.py: -------------------------------------------------------------------------------- 1 | # checkpoint saving 2 | checkpoint_config = dict(interval=1) 3 | # yapf:disable 4 | log_config = dict( 5 | interval=10, 6 | hooks=[ 7 | dict(type='TextLoggerHook'), 8 | # dict(type='TensorboardLoggerHook') 9 | ]) 10 | # yapf:enable 11 | 12 | dist_params = dict(backend='nccl') 13 | log_level = 'INFO' 14 | load_from = None 15 | resume_from = None 16 | workflow = [('train', 1)] 17 | -------------------------------------------------------------------------------- /configs/ofvl_ms/README.md: -------------------------------------------------------------------------------- 1 | # OFVL-MS: Once for Visual Localization across Multiple Indoor Scenes 2 | ![overall](https://github.com/mooncake199809/UFVL-Net/blob/main/assets/OFVL_overall.png) 3 | 4 | ## Model Zoo 5 | 6 | Coming soon! -------------------------------------------------------------------------------- /configs/ufvl_net/12scenes.py: -------------------------------------------------------------------------------- 1 | _base_ = [ 2 | '../_base_/default_runtime.py' 3 | ] 4 | 5 | dataset_type = 'ufvl_net.TWESCENES' 6 | # root='/home/dk/SCORE_Methods/EAAINet/data/' 7 | root='/home/dk/SCORE_Methods/EAAINet/data/' 8 | scene='office1/lounge' 9 | all_scene = ['apt1kitchen','apt1living','apt2bed','apt2kitchen', 10 | 'apt2living','apt2luke','office1gates362', 11 | 'office1gates381','office1lounge','office1manolis', 12 | 'office25a','office25b'] 13 | share_type = "kernel" # channel or kernel 14 | 15 | test_pipeline = [] 16 | eval_pipeline = [] 17 | custom_imports = dict(imports=['ufvl_net.models']) 18 | 19 | data = dict( 20 | samples_per_gpu=1, 21 | workers_per_gpu=1, 22 | train=dict( 23 | type=dataset_type, 24 | root=root, 25 | scene=scene, 26 | split='train'), 27 | val=dict( 28 | type=dataset_type, 29 | root=root, 30 | scene=scene, 31 | split='test'), 32 | test=dict( 33 | type=dataset_type, 34 | root=root, 35 | scene=scene, 36 | split='test')) 37 | 38 | model = dict( 39 | type='ufvl_net.FDANET', 40 | backbone=dict( 41 | type='SEResNet', 42 | depth=50, 43 | stem_channels=16, 44 | expansion = 1, 45 | strides=(1, 1, 2, 2), 46 | use_maxpool=False, 47 | num_stages=4, 48 | # conv_cfg=dict(type='Conv2d_share'), 49 | out_indices=(3, ), 50 | style='pytorch', 51 | drop_path_rate=0.1, 52 | ), 53 | head=dict( 54 | type='RegHead', 55 | in_channel=512), 56 | dataset="12Scenes") 57 | 58 | scene=scene.replace("/","") -------------------------------------------------------------------------------- /configs/ufvl_net/7scenes.py: -------------------------------------------------------------------------------- 1 | _base_ = [ 2 | '../_base_/default_runtime.py' 3 | ] 4 | 5 | dataset_type = 'ufvl_net.SevenScenes' 6 | # root='/home/dk/SCORE_Methods/EAAINet/data/' 7 | root='/home/dk/SCORE_Methods/EAAINet/data/' 8 | scene='chess' 9 | all_scene = ['chess', 'fire', 'heads', 'pumpkin', 'redkitchen', 'office', 'stairs'] 10 | share_type = "channel" # channel or kernel 11 | 12 | test_pipeline = [] 13 | eval_pipeline = [] 14 | custom_imports = dict(imports=['ufvl_net.models']) 15 | 16 | data = dict( 17 | samples_per_gpu=1, 18 | workers_per_gpu=1, 19 | train=dict( 20 | type=dataset_type, 21 | root=root, 22 | scene=scene, 23 | split='train'), 24 | val=dict( 25 | type=dataset_type, 26 | root=root, 27 | scene=scene, 28 | split='test'), 29 | test=dict( 30 | type=dataset_type, 31 | root=root, 32 | scene=scene, 33 | split='test')) 34 | 35 | model = dict( 36 | type='ufvl_net.FDANET', 37 | backbone=dict( 38 | type='SEResNet', 39 | depth=34, 40 | stem_channels=16, 41 | expansion = 1, 42 | strides=(1, 1, 2, 2), 43 | use_maxpool=False, 44 | num_stages=4, 45 | # conv_cfg=dict(type='Conv2d_share'), 46 | out_indices=(3, ), 47 | style='pytorch', 48 | drop_path_rate=0.1, 49 | ), 50 | head=dict( 51 | type='RegHead', 52 | in_channel=512), 53 | dataset="7Scenes") 54 | -------------------------------------------------------------------------------- /configs/ufvl_net/README.md: -------------------------------------------------------------------------------- 1 | # UFVL-Net: A Unified Framework for Visual Localization across Multiple Indoor Scenes 2 | 3 | ![overall](https://github.com/mooncake199809/UFVL-Net/blob/main/assets/overall.png) 4 | 5 | ## Model Zoo 6 | For evaluation, we provide the checkpoints of 7-Scenes dataset in [Google Drive](https://drive.google.com/drive/folders/1l4vWMz7mo49R1gMBxl932-DdavfhxiBO). 7 | For evaluation, we also provide the checkpoints of 12-Scenes dataset in [Google Drive](https://drive.google.com/drive/folders/1Yw-DskJD7hCPo-WIXfPvHI5mP5UgRgJ9). 8 | - Note: We integrate these models into a single one. You can do the evaluation following the description in *Quick Start - Test*). 9 | 10 | ## Quick Start 11 | 12 | We provide *Test* code of UFVL-Net as follows: 13 | 14 | ### Test 15 | To test our trained models, you need to put the downloaded model in `./weights`. 16 | To test a specific model in a specific scene, you need to modify the config file in ./configs/ufvl_net/7scenes.py or ./configs/ufvl_net/12scenes.py 17 | The structure of the config file is described as follow: 18 | ```buildoutcfg 19 | dataset_type: 'ufvl_net.SevenScenes' or 'ufvl_net.TWESCENES' 20 | root: the root path of the dataset 21 | scene: the scene name that you want to test 22 | share_type: the type of weight sharing ("channel" or "kernel") 23 | data: the config of the dataset 24 | model: the config of the model 25 | ``` 26 | - If you want to test UFVL-Net-M with channel-wise sharing policy on the chess scene of 7-Scenes dataset, you need to modify the lines 8, 10, and 39 as "chess", "channel", and depth=34. Then, you could use the following command: 27 | ```buildoutcfg 28 | python tools/test.py ./configs/ufvl_net/7scenes.py ./weights/34_channel_7scenes.pth --metrics accuracy 29 | ``` -------------------------------------------------------------------------------- /model-index.yml: -------------------------------------------------------------------------------- 1 | Import: 2 | - configs/mobilenet_v2/metafile.yml 3 | - configs/resnet/metafile.yml 4 | - configs/res2net/metafile.yml 5 | - configs/resnext/metafile.yml 6 | - configs/seresnet/metafile.yml 7 | - configs/shufflenet_v1/metafile.yml 8 | - configs/shufflenet_v2/metafile.yml 9 | - configs/swin_transformer/metafile.yml 10 | - configs/vgg/metafile.yml 11 | - configs/repvgg/metafile.yml 12 | - configs/tnt/metafile.yml 13 | - configs/vision_transformer/metafile.yml 14 | - configs/t2t_vit/metafile.yml 15 | - configs/mlp_mixer/metafile.yml 16 | - configs/conformer/metafile.yml 17 | - configs/regnet/metafile.yml 18 | - configs/deit/metafile.yml 19 | - configs/twins/metafile.yml 20 | - configs/efficientnet/metafile.yml 21 | - configs/convnext/metafile.yml 22 | - configs/hrnet/metafile.yml 23 | - configs/repmlp/metafile.yml 24 | - configs/wrn/metafile.yml 25 | - configs/van/metafile.yml 26 | - configs/cspnet/metafile.yml 27 | - configs/convmixer/metafile.yml 28 | - configs/densenet/metafile.yml 29 | - configs/poolformer/metafile.yml 30 | -------------------------------------------------------------------------------- /requirements/docs.txt: -------------------------------------------------------------------------------- 1 | docutils==0.17.1 2 | myst-parser 3 | -e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme 4 | sphinx==4.5.0 5 | sphinx-copybutton 6 | sphinx_markdown_tables 7 | -------------------------------------------------------------------------------- /requirements/mminstall.txt: -------------------------------------------------------------------------------- 1 | mmcv-full>=1.4.2,<1.6.0 2 | -------------------------------------------------------------------------------- /requirements/optional.txt: -------------------------------------------------------------------------------- 1 | albumentations>=0.3.2 --no-binary qudida,albumentations 2 | colorama 3 | requests 4 | rich 5 | -------------------------------------------------------------------------------- /requirements/readthedocs.txt: -------------------------------------------------------------------------------- 1 | mmcv>=1.4.2 2 | torch 3 | torchvision 4 | -------------------------------------------------------------------------------- /requirements/runtime.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | numpy 3 | packaging 4 | -------------------------------------------------------------------------------- /requirements/tests.txt: -------------------------------------------------------------------------------- 1 | codecov 2 | flake8 3 | interrogate 4 | isort==4.3.21 5 | mmdet 6 | pytest 7 | xdoctest >= 0.10.0 8 | yapf 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as osp 3 | import shutil 4 | import sys 5 | import warnings 6 | from setuptools import find_packages, setup 7 | 8 | 9 | def readme(): 10 | with open('README.md', encoding='utf-8') as f: 11 | content = f.read() 12 | return content 13 | 14 | 15 | def get_version(): 16 | version_file = '/home/dk/OFVL-VS2/mmcls/version.py' 17 | with open(version_file, 'r', encoding='utf-8') as f: 18 | exec(compile(f.read(), version_file, 'exec')) 19 | return locals()['__version__'] 20 | 21 | 22 | def parse_requirements(fname='requirements.txt', with_version=True): 23 | """Parse the package dependencies listed in a requirements file but strips 24 | specific versioning information. 25 | 26 | Args: 27 | fname (str): path to requirements file 28 | with_version (bool, default=False): if True include version specs 29 | 30 | Returns: 31 | List[str]: list of requirements items 32 | 33 | CommandLine: 34 | python -c "import setup; print(setup.parse_requirements())" 35 | """ 36 | import re 37 | import sys 38 | from os.path import exists 39 | require_fpath = fname 40 | 41 | def parse_line(line): 42 | """Parse information from a line in a requirements text file.""" 43 | if line.startswith('-r '): 44 | # Allow specifying requirements in other files 45 | target = line.split(' ')[1] 46 | for info in parse_require_file(target): 47 | yield info 48 | else: 49 | info = {'line': line} 50 | if line.startswith('-e '): 51 | info['package'] = line.split('#egg=')[1] 52 | else: 53 | # Remove versioning from the package 54 | pat = '(' + '|'.join(['>=', '==', '>']) + ')' 55 | parts = re.split(pat, line, maxsplit=1) 56 | parts = [p.strip() for p in parts] 57 | 58 | info['package'] = parts[0] 59 | if len(parts) > 1: 60 | op, rest = parts[1:] 61 | if ';' in rest: 62 | # Handle platform specific dependencies 63 | # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies 64 | version, platform_deps = map(str.strip, 65 | rest.split(';')) 66 | info['platform_deps'] = platform_deps 67 | else: 68 | version = rest # NOQA 69 | if '--' in version: 70 | # the `extras_require` doesn't accept options. 71 | version = version.split('--')[0].strip() 72 | info['version'] = (op, version) 73 | yield info 74 | 75 | def parse_require_file(fpath): 76 | with open(fpath, 'r') as f: 77 | for line in f.readlines(): 78 | line = line.strip() 79 | if line and not line.startswith('#'): 80 | for info in parse_line(line): 81 | yield info 82 | 83 | def gen_packages_items(): 84 | if exists(require_fpath): 85 | for info in parse_require_file(require_fpath): 86 | parts = [info['package']] 87 | if with_version and 'version' in info: 88 | parts.extend(info['version']) 89 | if not sys.version.startswith('3.4'): 90 | # apparently package_deps are broken in 3.4 91 | platform_deps = info.get('platform_deps') 92 | if platform_deps is not None: 93 | parts.append(';' + platform_deps) 94 | item = ''.join(parts) 95 | yield item 96 | 97 | packages = list(gen_packages_items()) 98 | return packages 99 | 100 | 101 | def add_mim_extension(): 102 | """Add extra files that are required to support MIM into the package. 103 | 104 | These files will be added by creating a symlink to the originals if the 105 | package is installed in `editable` mode (e.g. pip install -e .), or by 106 | copying from the originals otherwise. 107 | """ 108 | 109 | # parse installment mode 110 | if 'develop' in sys.argv: 111 | # installed by `pip install -e .` 112 | mode = 'symlink' 113 | elif 'sdist' in sys.argv or 'bdist_wheel' in sys.argv: 114 | # installed by `pip install .` 115 | # or create source distribution by `python setup.py sdist` 116 | mode = 'copy' 117 | else: 118 | return 119 | 120 | filenames = ['tools', 'configs', 'model-index.yml'] 121 | repo_path = osp.dirname(__file__) 122 | mim_path = osp.join(repo_path, 'ufvl_net', '.mim') 123 | os.makedirs(mim_path, exist_ok=True) 124 | 125 | for filename in filenames: 126 | if osp.exists(filename): 127 | src_path = osp.join(repo_path, filename) 128 | tar_path = osp.join(mim_path, filename) 129 | 130 | if osp.isfile(tar_path) or osp.islink(tar_path): 131 | os.remove(tar_path) 132 | elif osp.isdir(tar_path): 133 | shutil.rmtree(tar_path) 134 | 135 | if mode == 'symlink': 136 | src_relpath = osp.relpath(src_path, osp.dirname(tar_path)) 137 | try: 138 | os.symlink(src_relpath, tar_path) 139 | except OSError: 140 | # Creating a symbolic link on windows may raise an 141 | # `OSError: [WinError 1314]` due to privilege. If 142 | # the error happens, the src file will be copied 143 | mode = 'copy' 144 | warnings.warn( 145 | f'Failed to create a symbolic link for {src_relpath}, ' 146 | f'and it will be copied to {tar_path}') 147 | else: 148 | continue 149 | 150 | if mode == 'copy': 151 | if osp.isfile(src_path): 152 | shutil.copyfile(src_path, tar_path) 153 | elif osp.isdir(src_path): 154 | shutil.copytree(src_path, tar_path) 155 | else: 156 | warnings.warn(f'Cannot copy file {src_path}.') 157 | else: 158 | raise ValueError(f'Invalid mode {mode}') 159 | 160 | 161 | if __name__ == '__main__': 162 | add_mim_extension() 163 | setup( 164 | name='ufvl_net', 165 | version=get_version(), 166 | description='OpenMMLab Image Classification Toolbox and Benchmark', 167 | long_description=readme(), 168 | long_description_content_type='text/markdown', 169 | keywords='computer vision, image classification', 170 | packages=find_packages(exclude=('configs', 'tools', 'demo')), 171 | include_package_data=True, 172 | classifiers=[ 173 | 'Development Status :: 4 - Beta', 174 | 'License :: OSI Approved :: Apache Software License', 175 | 'Operating System :: OS Independent', 176 | 'Programming Language :: Python :: 3', 177 | 'Programming Language :: Python :: 3.6', 178 | 'Programming Language :: Python :: 3.7', 179 | 'Programming Language :: Python :: 3.8', 180 | 'Programming Language :: Python :: 3.9', 181 | 'Topic :: Scientific/Engineering :: Artificial Intelligence', 182 | ], 183 | url='https://github.com/open-mmlab/mmclassification', 184 | author='MMClassification Contributors', 185 | author_email='openmmlab@gmail.com', 186 | license='Apache License 2.0', 187 | install_requires=parse_requirements('requirements/runtime.txt'), 188 | extras_require={ 189 | 'all': parse_requirements('requirements.txt'), 190 | 'tests': parse_requirements('requirements/tests.txt'), 191 | 'optional': parse_requirements('requirements/optional.txt'), 192 | 'mim': parse_requirements('requirements/mminstall.txt'), 193 | }, 194 | zip_safe=False) 195 | -------------------------------------------------------------------------------- /tools/slurm_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -x 4 | 5 | PARTITION=$1 6 | JOB_NAME=$2 7 | CONFIG=$3 8 | CHECKPOINT=$4 9 | GPUS=${GPUS:-8} 10 | GPUS_PER_NODE=${GPUS_PER_NODE:-8} 11 | CPUS_PER_TASK=${CPUS_PER_TASK:-5} 12 | PY_ARGS=${@:5} 13 | SRUN_ARGS=${SRUN_ARGS:-""} 14 | 15 | PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ 16 | srun -p ${PARTITION} \ 17 | --job-name=${JOB_NAME} \ 18 | --gres=gpu:${GPUS_PER_NODE} \ 19 | --ntasks=${GPUS} \ 20 | --ntasks-per-node=${GPUS_PER_NODE} \ 21 | --cpus-per-task=${CPUS_PER_TASK} \ 22 | --kill-on-bad-exit=1 \ 23 | ${SRUN_ARGS} \ 24 | python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS} 25 | -------------------------------------------------------------------------------- /ufvl_net.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: ufvl-net 3 | Version: 0.23.1 4 | Summary: OpenMMLab Image Classification Toolbox and Benchmark 5 | Home-page: https://github.com/open-mmlab/mmclassification 6 | Author: MMClassification Contributors 7 | Author-email: openmmlab@gmail.com 8 | License: Apache License 2.0 9 | Keywords: computer vision,image classification 10 | Classifier: Development Status :: 4 - Beta 11 | Classifier: License :: OSI Approved :: Apache Software License 12 | Classifier: Operating System :: OS Independent 13 | Classifier: Programming Language :: Python :: 3 14 | Classifier: Programming Language :: Python :: 3.6 15 | Classifier: Programming Language :: Python :: 3.7 16 | Classifier: Programming Language :: Python :: 3.8 17 | Classifier: Programming Language :: Python :: 3.9 18 | Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence 19 | Description-Content-Type: text/markdown 20 | Provides-Extra: all 21 | Provides-Extra: tests 22 | Provides-Extra: optional 23 | Provides-Extra: mim 24 | 25 |
26 | 27 | 28 |
 
29 |
30 | OpenMMLab website 31 | 32 | 33 | HOT 34 | 35 | 36 |      37 | OpenMMLab platform 38 | 39 | 40 | TRY IT OUT 41 | 42 | 43 |
44 |
 
45 | 46 | [![PyPI](https://img.shields.io/pypi/v/mmcls)](https://pypi.org/project/mmcls) 47 | [![Docs](https://img.shields.io/badge/docs-latest-blue)](https://mmclassification.readthedocs.io/en/latest/) 48 | [![Build Status](https://github.com/open-mmlab/mmclassification/workflows/build/badge.svg)](https://github.com/open-mmlab/mmclassification/actions) 49 | [![codecov](https://codecov.io/gh/open-mmlab/mmclassification/branch/master/graph/badge.svg)](https://codecov.io/gh/open-mmlab/mmclassification) 50 | [![license](https://img.shields.io/github/license/open-mmlab/mmclassification.svg)](https://github.com/open-mmlab/mmclassification/blob/master/LICENSE) 51 | [![open issues](https://isitmaintained.com/badge/open/open-mmlab/mmclassification.svg)](https://github.com/open-mmlab/mmclassification/issues) 52 | [![issue resolution](https://isitmaintained.com/badge/resolution/open-mmlab/mmclassification.svg)](https://github.com/open-mmlab/mmclassification/issues) 53 | 54 | [📘 Documentation](https://mmclassification.readthedocs.io/en/latest/) | 55 | [🛠️ Installation](https://mmclassification.readthedocs.io/en/latest/install.html) | 56 | [👀 Model Zoo](https://mmclassification.readthedocs.io/en/latest/model_zoo.html) | 57 | [🆕 Update News](https://mmclassification.readthedocs.io/en/latest/changelog.html) | 58 | [🤔 Reporting Issues](https://github.com/open-mmlab/mmclassification/issues/new/choose) 59 | 60 |
61 | 62 | ## Introduction 63 | 64 | English | [简体中文](/README_zh-CN.md) 65 | 66 | MMClassification is an open source image classification toolbox based on PyTorch. It is 67 | a part of the [OpenMMLab](https://openmmlab.com/) project. 68 | 69 | The master branch works with **PyTorch 1.5+**. 70 | 71 |
72 | 73 |
74 | 75 | ### Major features 76 | 77 | - Various backbones and pretrained models 78 | - Bag of training tricks 79 | - Large-scale training configs 80 | - High efficiency and extensibility 81 | - Powerful toolkits 82 | 83 | ## What's new 84 | 85 | v0.23.0 was released in 1/5/2022. 86 | Highlights of the new version: 87 | 88 | - Support **DenseNet**, **VAN** and **PoolFormer**, and provide pre-trained models. 89 | - Support training on IPU. 90 | - New style API docs, welcome [view it](https://mmclassification.readthedocs.io/en/master/api/models.html). 91 | 92 | v0.22.0 was released in 30/3/2022. 93 | 94 | Highlights of the new version: 95 | 96 | - Support a series of **CSP Network**, such as CSP-ResNet, CSP-ResNeXt and CSP-DarkNet. 97 | - A new `CustomDataset` class to help you **build dataset of yourself**! 98 | - Support new backbones - **ConvMixer**, **RepMLP** and new dataset - **CUB dataset**. 99 | 100 | Please refer to [changelog.md](docs/en/changelog.md) for more details and other release history. 101 | 102 | ## Installation 103 | 104 | Below are quick steps for installation: 105 | 106 | ```shell 107 | conda create -n open-mmlab python=3.8 pytorch=1.10 cudatoolkit=11.3 torchvision -c pytorch -y 108 | conda activate open-mmlab 109 | pip3 install openmim 110 | mim install mmcv-full 111 | git clone https://github.com/open-mmlab/mmclassification.git 112 | cd mmclassification 113 | pip3 install -e . 114 | ``` 115 | 116 | Please refer to [install.md](https://mmclassification.readthedocs.io/en/latest/install.html) for more detailed installation and dataset preparation. 117 | 118 | ## Getting Started 119 | 120 | Please see [Getting Started](https://mmclassification.readthedocs.io/en/latest/getting_started.html) for the basic usage of MMClassification. There are also tutorials: 121 | 122 | - [Learn about Configs](https://mmclassification.readthedocs.io/en/latest/tutorials/config.html) 123 | - [Fine-tune Models](https://mmclassification.readthedocs.io/en/latest/tutorials/finetune.html) 124 | - [Add New Dataset](https://mmclassification.readthedocs.io/en/latest/tutorials/new_dataset.html) 125 | - [Customizie Data Pipeline](https://mmclassification.readthedocs.io/en/latest/tutorials/data_pipeline.html) 126 | - [Add New Modules](https://mmclassification.readthedocs.io/en/latest/tutorials/new_modules.html) 127 | - [Customizie Schedule](https://mmclassification.readthedocs.io/en/latest/tutorials/schedule.html) 128 | - [Customizie Runtime Settings](https://mmclassification.readthedocs.io/en/latest/tutorials/runtime.html) 129 | 130 | Colab tutorials are also provided: 131 | 132 | - Learn about MMClassification **Python API**: [Preview the notebook](https://github.com/open-mmlab/mmclassification/blob/master/docs/en/tutorials/MMClassification_python.ipynb) or directly [run on Colab](https://colab.research.google.com/github/open-mmlab/mmclassification/blob/master/docs/en/tutorials/MMClassification_python.ipynb). 133 | - Learn about MMClassification **CLI tools**: [Preview the notebook](https://github.com/open-mmlab/mmclassification/blob/master/docs/en/tutorials/MMClassification_tools.ipynb) or directly [run on Colab](https://colab.research.google.com/github/open-mmlab/mmclassification/blob/master/docs/en/tutorials/MMClassification_tools.ipynb). 134 | 135 | ## Model zoo 136 | 137 | Results and models are available in the [model zoo](https://mmclassification.readthedocs.io/en/latest/model_zoo.html). 138 | 139 |
140 | Supported backbones 141 | 142 | - [x] [VGG](https://github.com/open-mmlab/mmclassification/tree/master/configs/vgg) 143 | - [x] [ResNet](https://github.com/open-mmlab/mmclassification/tree/master/configs/resnet) 144 | - [x] [ResNeXt](https://github.com/open-mmlab/mmclassification/tree/master/configs/resnext) 145 | - [x] [SE-ResNet](https://github.com/open-mmlab/mmclassification/tree/master/configs/seresnet) 146 | - [x] [SE-ResNeXt](https://github.com/open-mmlab/mmclassification/tree/master/configs/seresnet) 147 | - [x] [RegNet](https://github.com/open-mmlab/mmclassification/tree/master/configs/regnet) 148 | - [x] [ShuffleNetV1](https://github.com/open-mmlab/mmclassification/tree/master/configs/shufflenet_v1) 149 | - [x] [ShuffleNetV2](https://github.com/open-mmlab/mmclassification/tree/master/configs/shufflenet_v2) 150 | - [x] [MobileNetV2](https://github.com/open-mmlab/mmclassification/tree/master/configs/mobilenet_v2) 151 | - [x] [MobileNetV3](https://github.com/open-mmlab/mmclassification/tree/master/configs/mobilenet_v3) 152 | - [x] [Swin-Transformer](https://github.com/open-mmlab/mmclassification/tree/master/configs/swin_transformer) 153 | - [x] [RepVGG](https://github.com/open-mmlab/mmclassification/tree/master/configs/repvgg) 154 | - [x] [Vision-Transformer](https://github.com/open-mmlab/mmclassification/tree/master/configs/vision_transformer) 155 | - [x] [Transformer-in-Transformer](https://github.com/open-mmlab/mmclassification/tree/master/configs/tnt) 156 | - [x] [Res2Net](https://github.com/open-mmlab/mmclassification/tree/master/configs/res2net) 157 | - [x] [MLP-Mixer](https://github.com/open-mmlab/mmclassification/tree/master/configs/mlp_mixer) 158 | - [x] [DeiT](https://github.com/open-mmlab/mmclassification/tree/master/configs/deit) 159 | - [x] [Conformer](https://github.com/open-mmlab/mmclassification/tree/master/configs/conformer) 160 | - [x] [T2T-ViT](https://github.com/open-mmlab/mmclassification/tree/master/configs/t2t_vit) 161 | - [x] [Twins](https://github.com/open-mmlab/mmclassification/tree/master/configs/twins) 162 | - [x] [EfficientNet](https://github.com/open-mmlab/mmclassification/tree/master/configs/efficientnet) 163 | - [x] [ConvNeXt](https://github.com/open-mmlab/mmclassification/tree/master/configs/convnext) 164 | - [x] [HRNet](https://github.com/open-mmlab/mmclassification/tree/master/configs/hrnet) 165 | - [x] [VAN](https://github.com/open-mmlab/mmclassification/tree/master/configs/van) 166 | - [x] [ConvMixer](https://github.com/open-mmlab/mmclassification/tree/master/configs/convmixer) 167 | - [x] [CSPNet](https://github.com/open-mmlab/mmclassification/tree/master/configs/cspnet) 168 | - [x] [PoolFormer](https://github.com/open-mmlab/mmclassification/tree/master/configs/poolformer) 169 | 170 |
171 | 172 | ## Contributing 173 | 174 | We appreciate all contributions to improve MMClassification. 175 | Please refer to [CONTRUBUTING.md](https://mmclassification.readthedocs.io/en/latest/community/CONTRIBUTING.html) for the contributing guideline. 176 | 177 | ## Acknowledgement 178 | 179 | MMClassification is an open source project that is contributed by researchers and engineers from various colleges and companies. We appreciate all the contributors who implement their methods or add new features, as well as users who give valuable feedbacks. 180 | We wish that the toolbox and benchmark could serve the growing research community by providing a flexible toolkit to reimplement existing methods and develop their own new classifiers. 181 | 182 | ## Citation 183 | 184 | If you find this project useful in your research, please consider cite: 185 | 186 | ```BibTeX 187 | @misc{2020mmclassification, 188 | title={OpenMMLab's Image Classification Toolbox and Benchmark}, 189 | author={MMClassification Contributors}, 190 | howpublished = {\url{https://github.com/open-mmlab/mmclassification}}, 191 | year={2020} 192 | } 193 | ``` 194 | 195 | ## License 196 | 197 | This project is released under the [Apache 2.0 license](LICENSE). 198 | 199 | ## Projects in OpenMMLab 200 | 201 | - [MMCV](https://github.com/open-mmlab/mmcv): OpenMMLab foundational library for computer vision. 202 | - [MIM](https://github.com/open-mmlab/mim): MIM installs OpenMMLab packages. 203 | - [MMClassification](https://github.com/open-mmlab/mmclassification): OpenMMLab image classification toolbox and benchmark. 204 | - [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab detection toolbox and benchmark. 205 | - [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab's next-generation platform for general 3D object detection. 206 | - [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab rotated object detection toolbox and benchmark. 207 | - [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab semantic segmentation toolbox and benchmark. 208 | - [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab text detection, recognition, and understanding toolbox. 209 | - [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab pose estimation toolbox and benchmark. 210 | - [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 3D human parametric model toolbox and benchmark. 211 | - [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab self-supervised learning toolbox and benchmark. 212 | - [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab model compression toolbox and benchmark. 213 | - [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab fewshot learning toolbox and benchmark. 214 | - [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab's next-generation action understanding toolbox and benchmark. 215 | - [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab video perception toolbox and benchmark. 216 | - [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab optical flow toolbox and benchmark. 217 | - [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab image and video editing toolbox. 218 | - [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab image and video generative models toolbox. 219 | - [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab model deployment framework. 220 | -------------------------------------------------------------------------------- /ufvl_net.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | README.md 2 | setup.py 3 | ufvl_net/__init__.py 4 | ufvl_net/version.py 5 | ufvl_net.egg-info/PKG-INFO 6 | ufvl_net.egg-info/SOURCES.txt 7 | ufvl_net.egg-info/dependency_links.txt 8 | ufvl_net.egg-info/not-zip-safe 9 | ufvl_net.egg-info/requires.txt 10 | ufvl_net.egg-info/top_level.txt 11 | ufvl_net/apis/__init__.py 12 | ufvl_net/apis/inference.py 13 | ufvl_net/apis/test.py 14 | ufvl_net/apis/train.py 15 | ufvl_net/datasets/__init__.py 16 | ufvl_net/datasets/builder.py 17 | ufvl_net/datasets/seven_scenes.py 18 | ufvl_net/models/__init__.py 19 | ufvl_net/models/builder.py 20 | ufvl_net/models/architecture/__init__.py 21 | ufvl_net/models/architecture/vis_loc.py 22 | ufvl_net/models/backbones/__init__.py 23 | ufvl_net/models/backbones/resnet.py 24 | ufvl_net/models/backbones/seresnet.py 25 | ufvl_net/models/heads/__init__.py 26 | ufvl_net/models/heads/score_head.py 27 | ufvl_net/utils/__init__.py 28 | ufvl_net/utils/collect_env.py 29 | ufvl_net/utils/logger.py 30 | ufvl_net/utils/setup_env.py -------------------------------------------------------------------------------- /ufvl_net.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ufvl_net.egg-info/not-zip-safe: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ufvl_net.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | numpy 3 | packaging 4 | 5 | [all] 6 | 7 | [mim] 8 | mmcv-full<1.6.0,>=1.4.2 9 | 10 | [optional] 11 | albumentations>=0.3.2 12 | colorama 13 | requests 14 | rich 15 | 16 | [tests] 17 | codecov 18 | flake8 19 | interrogate 20 | isort==4.3.21 21 | mmdet 22 | pytest 23 | xdoctest>=0.10.0 24 | yapf 25 | -------------------------------------------------------------------------------- /ufvl_net.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | ufvl_net 2 | -------------------------------------------------------------------------------- /ufvl_net/.mim/configs: -------------------------------------------------------------------------------- 1 | ../../configs -------------------------------------------------------------------------------- /ufvl_net/.mim/model-index.yml: -------------------------------------------------------------------------------- 1 | ../../model-index.yml -------------------------------------------------------------------------------- /ufvl_net/.mim/tools: -------------------------------------------------------------------------------- 1 | ../../tools -------------------------------------------------------------------------------- /ufvl_net/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import warnings 3 | 4 | import mmcv 5 | from packaging.version import parse 6 | 7 | from .version import __version__ 8 | 9 | 10 | def digit_version(version_str: str, length: int = 4): 11 | """Convert a version string into a tuple of integers. 12 | 13 | This method is usually used for comparing two versions. For pre-release 14 | versions: alpha < beta < rc. 15 | 16 | Args: 17 | version_str (str): The version string. 18 | length (int): The maximum number of version levels. Default: 4. 19 | 20 | Returns: 21 | tuple[int]: The version info in digits (integers). 22 | """ 23 | version = parse(version_str) 24 | assert version.release, f'failed to parse version {version_str}' 25 | release = list(version.release) 26 | release = release[:length] 27 | if len(release) < length: 28 | release = release + [0] * (length - len(release)) 29 | if version.is_prerelease: 30 | mapping = {'a': -3, 'b': -2, 'rc': -1} 31 | val = -4 32 | # version.pre can be None 33 | if version.pre: 34 | if version.pre[0] not in mapping: 35 | warnings.warn(f'unknown prerelease version {version.pre[0]}, ' 36 | 'version checking may go wrong') 37 | else: 38 | val = mapping[version.pre[0]] 39 | release.extend([val, version.pre[-1]]) 40 | else: 41 | release.extend([val, 0]) 42 | 43 | elif version.is_postrelease: 44 | release.extend([1, version.post]) 45 | else: 46 | release.extend([0, 0]) 47 | return tuple(release) 48 | 49 | 50 | mmcv_minimum_version = '1.4.2' 51 | mmcv_maximum_version = '1.6.0' 52 | mmcv_version = digit_version(mmcv.__version__) 53 | 54 | 55 | assert (mmcv_version >= digit_version(mmcv_minimum_version) 56 | and mmcv_version <= digit_version(mmcv_maximum_version)), \ 57 | f'MMCV=={mmcv.__version__} is used but incompatible. ' \ 58 | f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.' 59 | 60 | __all__ = ['__version__', 'digit_version'] 61 | -------------------------------------------------------------------------------- /ufvl_net/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/__pycache__/version.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/__pycache__/version.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/apis/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .inference import inference_model, init_model, show_result_pyplot 3 | from .test import multi_gpu_test, single_gpu_test 4 | from .train import init_random_seed, set_random_seed 5 | 6 | __all__ = [ 7 | 'set_random_seed', 'init_model', 'inference_model', 8 | 'multi_gpu_test', 'single_gpu_test', 'show_result_pyplot', 9 | 'init_random_seed' 10 | ] 11 | -------------------------------------------------------------------------------- /ufvl_net/apis/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/apis/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/apis/__pycache__/inference.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/apis/__pycache__/inference.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/apis/__pycache__/test.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/apis/__pycache__/test.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/apis/__pycache__/train.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/apis/__pycache__/train.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/apis/inference.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import warnings 3 | 4 | import mmcv 5 | import numpy as np 6 | import torch 7 | from mmcv.parallel import collate, scatter 8 | from mmcv.runner import load_checkpoint 9 | 10 | from ufvl_net.datasets.piplines import Compose 11 | from ufvl_net.models import build_architecture 12 | 13 | 14 | def init_model(config, checkpoint=None, device='cuda:0', options=None): 15 | """Initialize a classifier from config file. 16 | 17 | Args: 18 | config (str or :obj:`mmcv.Config`): Config file path or the config 19 | object. 20 | checkpoint (str, optional): Checkpoint path. If left as None, the model 21 | will not load any weights. 22 | options (dict): Options to override some settings in the used config. 23 | 24 | Returns: 25 | nn.Module: The constructed classifier. 26 | """ 27 | if isinstance(config, str): 28 | config = mmcv.Config.fromfile(config) 29 | elif not isinstance(config, mmcv.Config): 30 | raise TypeError('config must be a filename or Config object, ' 31 | f'but got {type(config)}') 32 | if options is not None: 33 | config.merge_from_dict(options) 34 | config.model.pretrained = None 35 | model = build_classifier(config.model) 36 | if checkpoint is not None: 37 | # Mapping the weights to GPU may cause unexpected video memory leak 38 | # which refers to https://github.com/open-mmlab/mmdetection/pull/6405 39 | checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') 40 | if 'CLASSES' in checkpoint.get('meta', {}): 41 | model.CLASSES = checkpoint['meta']['CLASSES'] 42 | else: 43 | from mmcls.datasets import ImageNet 44 | warnings.simplefilter('once') 45 | warnings.warn('Class names are not saved in the checkpoint\'s ' 46 | 'meta data, use imagenet by default.') 47 | model.CLASSES = ImageNet.CLASSES 48 | model.cfg = config # save the config in the model for convenience 49 | model.to(device) 50 | model.eval() 51 | return model 52 | 53 | 54 | def inference_model(model, img): 55 | """Inference image(s) with the classifier. 56 | 57 | Args: 58 | model (nn.Module): The loaded classifier. 59 | img (str/ndarray): The image filename or loaded image. 60 | 61 | Returns: 62 | result (dict): The classification results that contains 63 | `class_name`, `pred_label` and `pred_score`. 64 | """ 65 | cfg = model.cfg 66 | device = next(model.parameters()).device # model device 67 | # build the data pipeline 68 | if isinstance(img, str): 69 | if cfg.data.test.pipeline[0]['type'] != 'LoadImageFromFile': 70 | cfg.data.test.pipeline.insert(0, dict(type='LoadImageFromFile')) 71 | data = dict(img_info=dict(filename=img), img_prefix=None) 72 | else: 73 | if cfg.data.test.pipeline[0]['type'] == 'LoadImageFromFile': 74 | cfg.data.test.pipeline.pop(0) 75 | data = dict(img=img) 76 | test_pipeline = Compose(cfg.data.test.pipeline) 77 | data = test_pipeline(data) 78 | data = collate([data], samples_per_gpu=1) 79 | if next(model.parameters()).is_cuda: 80 | # scatter to specified GPU 81 | data = scatter(data, [device])[0] 82 | 83 | # forward the model 84 | with torch.no_grad(): 85 | scores = model(return_loss=False, **data) 86 | pred_score = np.max(scores, axis=1)[0] 87 | pred_label = np.argmax(scores, axis=1)[0] 88 | result = {'pred_label': pred_label, 'pred_score': float(pred_score)} 89 | result['pred_class'] = model.CLASSES[result['pred_label']] 90 | return result 91 | 92 | 93 | def show_result_pyplot(model, 94 | img, 95 | result, 96 | fig_size=(15, 10), 97 | title='result', 98 | wait_time=0): 99 | """Visualize the classification results on the image. 100 | 101 | Args: 102 | model (nn.Module): The loaded classifier. 103 | img (str or np.ndarray): Image filename or loaded image. 104 | result (list): The classification result. 105 | fig_size (tuple): Figure size of the pyplot figure. 106 | Defaults to (15, 10). 107 | title (str): Title of the pyplot figure. 108 | Defaults to 'result'. 109 | wait_time (int): How many seconds to display the image. 110 | Defaults to 0. 111 | """ 112 | if hasattr(model, 'module'): 113 | model = model.module 114 | model.show_result( 115 | img, 116 | result, 117 | show=True, 118 | fig_size=fig_size, 119 | win_name=title, 120 | wait_time=wait_time) 121 | -------------------------------------------------------------------------------- /ufvl_net/apis/test.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import os.path as osp 3 | import pickle 4 | import shutil 5 | import tempfile 6 | import time 7 | 8 | import mmcv 9 | import numpy as np 10 | import torch 11 | import torch.distributed as dist 12 | from mmcv.image import tensor2imgs 13 | from mmcv.runner import get_dist_info 14 | 15 | 16 | def single_gpu_test(model, 17 | data_loader, 18 | show=False, 19 | out_dir=None, 20 | **show_kwargs): 21 | """Test model with local single gpu. 22 | 23 | This method tests model with a single gpu and supports showing results. 24 | 25 | Args: 26 | model (:obj:`torch.nn.Module`): Model to be tested. 27 | data_loader (:obj:`torch.utils.data.DataLoader`): Pytorch data loader. 28 | show (bool): Whether to show the test results. Defaults to False. 29 | out_dir (str): The output directory of result plots of all samples. 30 | Defaults to None, which means not to write output files. 31 | **show_kwargs: Any other keyword arguments for showing results. 32 | 33 | Returns: 34 | list: The prediction results. 35 | """ 36 | model.eval() 37 | results = [] 38 | transl_err_list = [] 39 | rot_err_list = [] 40 | time_list = [] 41 | for i, data in enumerate(data_loader): 42 | with torch.no_grad(): 43 | start_time = time.time() 44 | result = model(return_loss=False, **data) 45 | time_list.append(time.time() - start_time) 46 | print(np.min(np.array(time_list))) 47 | 48 | transl_err_list.append(result['trans_error_med']) 49 | rot_err_list.append(result['rot_err_med']) 50 | res_ = np.array([transl_err_list, rot_err_list]).T 51 | print('Median pose error: {}m, {}\u00b0'.format(np.median(res_[:, 0]), 52 | np.median(res_[:, 1]))) 53 | print('Average pose error: {}m, {}\u00b0'.format(np.mean(res_[:, 0]), 54 | np.mean(res_[:, 1]))) 55 | print('stddev: {}m, {}\u00b0'.format(np.std(res_[:, 0], ddof=1), 56 | np.std(res_[:, 1], ddof=1))) 57 | print("ACC:{}", np.sum((res_[:, 0] <= 0.050) * (res_[:, 1] <= 5)) * 1. / len(res_) * 100) 58 | results.append(result) 59 | return results 60 | 61 | 62 | def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): 63 | """Test model with multiple gpus. 64 | 65 | This method tests model with multiple gpus and collects the results 66 | under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' 67 | it encodes results to gpu tensors and use gpu communication for results 68 | collection. On cpu mode it saves the results on different gpus to 'tmpdir' 69 | and collects them by the rank 0 worker. 70 | 71 | Args: 72 | model (nn.Module): Model to be tested. 73 | data_loader (nn.Dataloader): Pytorch data loader. 74 | tmpdir (str): Path of directory to save the temporary results from 75 | different gpus under cpu mode. 76 | gpu_collect (bool): Option to use either gpu or cpu to collect results. 77 | 78 | Returns: 79 | list: The prediction results. 80 | """ 81 | model.eval() 82 | results = [] 83 | dataset = data_loader.dataset 84 | rank, world_size = get_dist_info() 85 | if rank == 0: 86 | # Check if tmpdir is valid for cpu_collect 87 | if (not gpu_collect) and (tmpdir is not None and osp.exists(tmpdir)): 88 | raise OSError((f'The tmpdir {tmpdir} already exists.', 89 | ' Since tmpdir will be deleted after testing,', 90 | ' please make sure you specify an empty one.')) 91 | prog_bar = mmcv.ProgressBar(len(dataset)) 92 | time.sleep(2) 93 | dist.barrier() 94 | for i, data in enumerate(data_loader): 95 | with torch.no_grad(): 96 | result = model(return_loss=False, **data) 97 | if isinstance(result, list): 98 | results.extend(result) 99 | else: 100 | results.append(result) 101 | 102 | if rank == 0: 103 | batch_size = data['img'].size(0) 104 | for _ in range(batch_size * world_size): 105 | prog_bar.update() 106 | 107 | # collect results from all ranks 108 | if gpu_collect: 109 | results = collect_results_gpu(results, len(dataset)) 110 | else: 111 | results = collect_results_cpu(results, len(dataset), tmpdir) 112 | return results 113 | 114 | 115 | def collect_results_cpu(result_part, size, tmpdir=None): 116 | rank, world_size = get_dist_info() 117 | # create a tmp dir if it is not specified 118 | if tmpdir is None: 119 | MAX_LEN = 512 120 | # 32 is whitespace 121 | dir_tensor = torch.full((MAX_LEN, ), 122 | 32, 123 | dtype=torch.uint8, 124 | device='cuda') 125 | if rank == 0: 126 | mmcv.mkdir_or_exist('.dist_test') 127 | tmpdir = tempfile.mkdtemp(dir='.dist_test') 128 | tmpdir = torch.tensor( 129 | bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') 130 | dir_tensor[:len(tmpdir)] = tmpdir 131 | dist.broadcast(dir_tensor, 0) 132 | tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() 133 | else: 134 | mmcv.mkdir_or_exist(tmpdir) 135 | # dump the part result to the dir 136 | mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) 137 | dist.barrier() 138 | # collect all parts 139 | if rank != 0: 140 | return None 141 | else: 142 | # load results of all parts from tmp dir 143 | part_list = [] 144 | for i in range(world_size): 145 | part_file = osp.join(tmpdir, f'part_{i}.pkl') 146 | part_result = mmcv.load(part_file) 147 | part_list.append(part_result) 148 | # sort the results 149 | ordered_results = [] 150 | for res in zip(*part_list): 151 | ordered_results.extend(list(res)) 152 | # the dataloader may pad some samples 153 | ordered_results = ordered_results[:size] 154 | # remove tmp dir 155 | shutil.rmtree(tmpdir) 156 | return ordered_results 157 | 158 | 159 | def collect_results_gpu(result_part, size): 160 | rank, world_size = get_dist_info() 161 | # dump result part to tensor with pickle 162 | part_tensor = torch.tensor( 163 | bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') 164 | # gather all result part tensor shape 165 | shape_tensor = torch.tensor(part_tensor.shape, device='cuda') 166 | shape_list = [shape_tensor.clone() for _ in range(world_size)] 167 | dist.all_gather(shape_list, shape_tensor) 168 | # padding result part tensor to max length 169 | shape_max = torch.tensor(shape_list).max() 170 | part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') 171 | part_send[:shape_tensor[0]] = part_tensor 172 | part_recv_list = [ 173 | part_tensor.new_zeros(shape_max) for _ in range(world_size) 174 | ] 175 | # gather all result part 176 | dist.all_gather(part_recv_list, part_send) 177 | 178 | if rank == 0: 179 | part_list = [] 180 | for recv, shape in zip(part_recv_list, shape_list): 181 | part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) 182 | part_list.append(part_result) 183 | # sort the results 184 | ordered_results = [] 185 | for res in zip(*part_list): 186 | ordered_results.extend(list(res)) 187 | # the dataloader may pad some samples 188 | ordered_results = ordered_results[:size] 189 | return ordered_results 190 | -------------------------------------------------------------------------------- /ufvl_net/apis/train.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import random 3 | import warnings 4 | 5 | import numpy as np 6 | import torch 7 | import torch.distributed as dist 8 | from mmcv.parallel import MMDataParallel, MMDistributedDataParallel 9 | from mmcv.runner import (DistSamplerSeedHook, Fp16OptimizerHook, 10 | build_optimizer, build_runner, get_dist_info) 11 | from mmcv.runner.hooks import DistEvalHook, EvalHook 12 | 13 | from ufvl_net.datasets import build_dataloader, build_dataset 14 | from ufvl_net.utils import get_root_logger 15 | 16 | 17 | def init_random_seed(seed=None, device='cuda'): 18 | """Initialize random seed. 19 | 20 | If the seed is not set, the seed will be automatically randomized, 21 | and then broadcast to all processes to prevent some potential bugs. 22 | 23 | Args: 24 | seed (int, Optional): The seed. Default to None. 25 | device (str): The device where the seed will be put on. 26 | Default to 'cuda'. 27 | 28 | Returns: 29 | int: Seed to be used. 30 | """ 31 | if seed is not None: 32 | return seed 33 | 34 | # Make sure all ranks share the same random seed to prevent 35 | # some potential bugs. Please refer to 36 | # https://github.com/open-mmlab/mmdetection/issues/6339 37 | rank, world_size = get_dist_info() 38 | seed = np.random.randint(2**31) 39 | if world_size == 1: 40 | return seed 41 | 42 | if rank == 0: 43 | random_num = torch.tensor(seed, dtype=torch.int32, device=device) 44 | else: 45 | random_num = torch.tensor(0, dtype=torch.int32, device=device) 46 | dist.broadcast(random_num, src=0) 47 | return random_num.item() 48 | 49 | 50 | def set_random_seed(seed, deterministic=False): 51 | """Set random seed. 52 | 53 | Args: 54 | seed (int): Seed to be used. 55 | deterministic (bool): Whether to set the deterministic option for 56 | CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` 57 | to True and `torch.backends.cudnn.benchmark` to False. 58 | Default: False. 59 | """ 60 | random.seed(seed) 61 | np.random.seed(seed) 62 | torch.manual_seed(seed) 63 | torch.cuda.manual_seed_all(seed) 64 | if deterministic: 65 | torch.backends.cudnn.deterministic = True 66 | torch.backends.cudnn.benchmark = False 67 | 68 | -------------------------------------------------------------------------------- /ufvl_net/cnn/__init__.py: -------------------------------------------------------------------------------- 1 | from .conv2d_share import Conv2d_share, BatchNorm2d_share 2 | 3 | __all__ = [ 4 | 'Conv2d_share', 'BatchNorm2d_share' 5 | ] 6 | -------------------------------------------------------------------------------- /ufvl_net/cnn/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/cnn/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/cnn/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/cnn/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /ufvl_net/cnn/__pycache__/conv2d_share.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/cnn/__pycache__/conv2d_share.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/cnn/__pycache__/conv2d_share.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/cnn/__pycache__/conv2d_share.cpython-38.pyc -------------------------------------------------------------------------------- /ufvl_net/cnn/cnn/__init__.py: -------------------------------------------------------------------------------- 1 | from .conv2d_share import Conv2d_share, BatchNorm2d_share 2 | 3 | __all__ = [ 4 | 'Conv2d_share', 'BatchNorm2d_share' 5 | ] 6 | -------------------------------------------------------------------------------- /ufvl_net/cnn/cnn/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/cnn/cnn/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/cnn/cnn/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/cnn/cnn/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /ufvl_net/cnn/cnn/__pycache__/conv2d_share.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/cnn/cnn/__pycache__/conv2d_share.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/cnn/cnn/__pycache__/conv2d_share.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/cnn/cnn/__pycache__/conv2d_share.cpython-38.pyc -------------------------------------------------------------------------------- /ufvl_net/cnn/cnn/conv2d_share.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | from mmcv.cnn import CONV_LAYERS, NORM_LAYERS 5 | 6 | THRESHOLD = 0.5 7 | 8 | 9 | @CONV_LAYERS.register_module() 10 | class Conv2d_share(nn.Conv2d): 11 | def __init__(self, in_channels, out_channels, kernel_size, share_type=None, 12 | stride=1, padding=0, dilation=1, groups=1, bias=True, 13 | padding_mode='zeros'): 14 | super().__init__(in_channels, out_channels, kernel_size, stride, padding, dilation, groups, bias, padding_mode 15 | ) 16 | self.share_type = 'channel_wise' 17 | self.register_parameter('specific_weight', nn.Parameter(self.weight.clone())) 18 | if self.bias is not None: 19 | self.register_parameter('specific_bias', nn.Parameter(self.bias.clone())) 20 | 21 | # TODO: ablate one, zero and random 22 | # import pdb; pdb.set_trace() 23 | if self.share_type == 'channel_wise': 24 | self.score = nn.Parameter(torch.rand(self.weight.size(1)).cuda()) 25 | elif self.share_type == 'kernel_wise': 26 | self.score = nn.Parameter(torch.rand((self.weight.size(2), self.weight.size(3))).cuda()) 27 | else: 28 | self.score = nn.Parameter(torch.rand(1).cuda()) 29 | 30 | def forward(self, input): 31 | if self.share_type == 'channel_wise': 32 | score = binarizer_fn(self.score).unsqueeze(0).unsqueeze(-1).unsqueeze(-1) 33 | elif self.share_type == 'kernel_wise': 34 | score = binarizer_fn(self.score).unsqueeze(0).unsqueeze(0) 35 | else: 36 | score = binarizer_fn(self.score) 37 | 38 | weight = score * self.weight + (1 - score) * self.specific_weight 39 | if self.bias is not None: 40 | bias = score * self.bias + (1 - score) * self.specific_bias 41 | else: 42 | bias = None 43 | return self._conv_forward(input, weight) 44 | 45 | 46 | @NORM_LAYERS.register_module() 47 | class BatchNorm2d_share(nn.BatchNorm2d): 48 | def __init__(self, num_features, eps=0.00001, momentum=0.1, affine=True, 49 | track_running_stats=True, device=None, dtype=None): 50 | super().__init__(num_features, eps, momentum, affine, track_running_stats, device, dtype) 51 | self.register_parameter('specific_weight', nn.Parameter(self.weight.clone())) 52 | if self.bias is not None: 53 | self.register_parameter('specific_bias', nn.Parameter(self.bias.clone())) 54 | self.score = nn.Parameter(torch.rand(1).cuda()) 55 | 56 | def forward(self, input): 57 | self._check_input_dim(input) 58 | 59 | # exponential_average_factor is set to self.momentum 60 | # (when it is available) only so that it gets updated 61 | # in ONNX graph when this node is exported to ONNX. 62 | if self.momentum is None: 63 | exponential_average_factor = 0.0 64 | else: 65 | exponential_average_factor = self.momentum 66 | 67 | if self.training and self.track_running_stats: 68 | # TODO: if statement only here to tell the jit to skip emitting this when it is None 69 | if self.num_batches_tracked is not None: # type: ignore[has-type] 70 | self.num_batches_tracked = self.num_batches_tracked + 1 # type: ignore[has-type] 71 | if self.momentum is None: # use cumulative moving average 72 | exponential_average_factor = 1.0 / float(self.num_batches_tracked) 73 | else: # use exponential moving average 74 | exponential_average_factor = self.momentum 75 | 76 | r""" 77 | Decide whether the mini-batch stats should be used for normalization rather than the buffers. 78 | Mini-batch stats are used in training mode, and in eval mode when buffers are None. 79 | """ 80 | if self.training: 81 | bn_training = True 82 | else: 83 | bn_training = (self.running_mean is None) and (self.running_var is None) 84 | 85 | r""" 86 | Buffers are only updated if they are to be tracked and we are in training mode. Thus they only need to be 87 | passed when the update should occur (i.e. in training mode when they are tracked), or when buffer stats are 88 | used for normalization (i.e. in eval mode when buffers are not None). 89 | """ 90 | 91 | score = binarizer_fn(self.score) 92 | 93 | weight = score * self.weight + (1 - score) * self.specific_weight 94 | if self.bias is not None: 95 | bias = score * self.bias + (1 - score) * self.specific_bias 96 | else: 97 | bias = None 98 | 99 | return F.batch_norm( 100 | input, 101 | # If buffers are not to be tracked, ensure that they won't be updated 102 | self.running_mean 103 | if not self.training or self.track_running_stats 104 | else None, 105 | self.running_var if not self.training or self.track_running_stats else None, 106 | weight, 107 | bias, 108 | bn_training, 109 | exponential_average_factor, 110 | self.eps, 111 | ) 112 | 113 | 114 | class BinarizerFn(torch.autograd.Function): 115 | """Binarizes {0, 1} a real valued tensor.""" 116 | 117 | @staticmethod 118 | def forward(ctx, inputs, threshold=THRESHOLD): 119 | outputs = inputs.clone() 120 | outputs[inputs.le(threshold)] = 1 121 | outputs[inputs.gt(threshold)] = 0 122 | return outputs 123 | 124 | @staticmethod 125 | def backward(self, gradOutput): 126 | return gradOutput, None 127 | 128 | 129 | binarizer_fn = BinarizerFn.apply -------------------------------------------------------------------------------- /ufvl_net/cnn/conv2d_share.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | from mmcv.cnn import CONV_LAYERS, NORM_LAYERS 5 | 6 | THRESHOLD = 0.5 7 | 8 | 9 | @CONV_LAYERS.register_module() 10 | class Conv2d_share(nn.Conv2d): 11 | def __init__(self, in_channels, out_channels, kernel_size, share_type=None, 12 | stride=1, padding=0, dilation=1, groups=1, bias=True, 13 | padding_mode='zeros'): 14 | super().__init__(in_channels, out_channels, kernel_size, stride, padding, dilation, groups, bias, padding_mode 15 | ) 16 | self.share_type = 'channel_wise' 17 | self.register_parameter('specific_weight', nn.Parameter(self.weight.clone())) 18 | if self.bias is not None: 19 | self.register_parameter('specific_bias', nn.Parameter(self.bias.clone())) 20 | 21 | # TODO: ablate one, zero and random 22 | # import pdb; pdb.set_trace() 23 | if self.share_type == 'channel_wise': 24 | self.score = nn.Parameter(torch.rand(self.weight.size(1)).cuda()) 25 | elif self.share_type == 'kernel_wise': 26 | self.score = nn.Parameter(torch.rand((self.weight.size(2), self.weight.size(3))).cuda()) 27 | else: 28 | self.score = nn.Parameter(torch.rand(1).cuda()) 29 | 30 | def forward(self, input): 31 | if self.share_type == 'channel_wise': 32 | score = binarizer_fn(self.score).unsqueeze(0).unsqueeze(-1).unsqueeze(-1) 33 | elif self.share_type == 'kernel_wise': 34 | score = binarizer_fn(self.score).unsqueeze(0).unsqueeze(0) 35 | else: 36 | score = binarizer_fn(self.score) 37 | 38 | weight = score * self.weight + (1 - score) * self.specific_weight 39 | if self.bias is not None: 40 | bias = score * self.bias + (1 - score) * self.specific_bias 41 | else: 42 | bias = None 43 | return self._conv_forward(input, weight) 44 | 45 | 46 | @NORM_LAYERS.register_module() 47 | class BatchNorm2d_share(nn.BatchNorm2d): 48 | def __init__(self, num_features, eps=0.00001, momentum=0.1, affine=True, 49 | track_running_stats=True, device=None, dtype=None): 50 | super().__init__(num_features, eps, momentum, affine, track_running_stats, device, dtype) 51 | self.register_parameter('specific_weight', nn.Parameter(self.weight.clone())) 52 | if self.bias is not None: 53 | self.register_parameter('specific_bias', nn.Parameter(self.bias.clone())) 54 | self.score = nn.Parameter(torch.rand(1).cuda()) 55 | 56 | def forward(self, input): 57 | self._check_input_dim(input) 58 | 59 | # exponential_average_factor is set to self.momentum 60 | # (when it is available) only so that it gets updated 61 | # in ONNX graph when this node is exported to ONNX. 62 | if self.momentum is None: 63 | exponential_average_factor = 0.0 64 | else: 65 | exponential_average_factor = self.momentum 66 | 67 | if self.training and self.track_running_stats: 68 | # TODO: if statement only here to tell the jit to skip emitting this when it is None 69 | if self.num_batches_tracked is not None: # type: ignore[has-type] 70 | self.num_batches_tracked = self.num_batches_tracked + 1 # type: ignore[has-type] 71 | if self.momentum is None: # use cumulative moving average 72 | exponential_average_factor = 1.0 / float(self.num_batches_tracked) 73 | else: # use exponential moving average 74 | exponential_average_factor = self.momentum 75 | 76 | r""" 77 | Decide whether the mini-batch stats should be used for normalization rather than the buffers. 78 | Mini-batch stats are used in training mode, and in eval mode when buffers are None. 79 | """ 80 | if self.training: 81 | bn_training = True 82 | else: 83 | bn_training = (self.running_mean is None) and (self.running_var is None) 84 | 85 | r""" 86 | Buffers are only updated if they are to be tracked and we are in training mode. Thus they only need to be 87 | passed when the update should occur (i.e. in training mode when they are tracked), or when buffer stats are 88 | used for normalization (i.e. in eval mode when buffers are not None). 89 | """ 90 | 91 | score = binarizer_fn(self.score) 92 | 93 | weight = score * self.weight + (1 - score) * self.specific_weight 94 | if self.bias is not None: 95 | bias = score * self.bias + (1 - score) * self.specific_bias 96 | else: 97 | bias = None 98 | 99 | return F.batch_norm( 100 | input, 101 | # If buffers are not to be tracked, ensure that they won't be updated 102 | self.running_mean 103 | if not self.training or self.track_running_stats 104 | else None, 105 | self.running_var if not self.training or self.track_running_stats else None, 106 | weight, 107 | bias, 108 | bn_training, 109 | exponential_average_factor, 110 | self.eps, 111 | ) 112 | 113 | 114 | class BinarizerFn(torch.autograd.Function): 115 | """Binarizes {0, 1} a real valued tensor.""" 116 | 117 | @staticmethod 118 | def forward(ctx, inputs, threshold=THRESHOLD): 119 | outputs = inputs.clone() 120 | outputs[inputs.le(threshold)] = 1 121 | outputs[inputs.gt(threshold)] = 0 122 | return outputs 123 | 124 | @staticmethod 125 | def backward(ctx, gradOutput): 126 | return gradOutput, None 127 | 128 | 129 | binarizer_fn = BinarizerFn.apply 130 | -------------------------------------------------------------------------------- /ufvl_net/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .builder import (DATASETS, PIPELINES, SAMPLERS, build_dataloader, 3 | build_dataset, build_sampler) 4 | from .seven_scenes import SevenScenes 5 | from .twelvescenes import TWESCENES 6 | 7 | __all__ = ['SevenScenes', 'TWESCENES', 'DATASETS', 'PIPELINES', 'SAMPLERS', 8 | 'build_dataloader', 'build_dataset', 'build_sampler'] 9 | -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/base_dataset.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/base_dataset.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/builder.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/builder.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/cambridge.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/cambridge.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/cifar.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/cifar.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/cub.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/cub.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/custom.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/custom.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/dataset_wrappers.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/dataset_wrappers.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/imagenet.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/imagenet.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/imagenet21k.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/imagenet21k.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/mnist.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/mnist.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/multi_label.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/multi_label.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/seven_scenes.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/seven_scenes.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/twelve_scenes.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/twelve_scenes.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/twelvescenes.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/twelvescenes.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/utils.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/utils.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/__pycache__/voc.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/__pycache__/voc.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/builder.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import copy 3 | import platform 4 | import random 5 | from functools import partial 6 | 7 | import numpy as np 8 | import torch 9 | from mmcv.parallel import collate 10 | from mmcv.runner import get_dist_info 11 | from mmcv.utils import Registry, build_from_cfg, digit_version 12 | from torch.utils.data import DataLoader 13 | 14 | try: 15 | from mmcv.utils import IS_IPU_AVAILABLE 16 | except ImportError: 17 | IS_IPU_AVAILABLE = False 18 | 19 | if platform.system() != 'Windows': 20 | # https://github.com/pytorch/pytorch/issues/973 21 | import resource 22 | rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) 23 | hard_limit = rlimit[1] 24 | soft_limit = min(4096, hard_limit) 25 | resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) 26 | 27 | DATASETS = Registry('dataset') 28 | PIPELINES = Registry('pipeline') 29 | SAMPLERS = Registry('sampler') 30 | 31 | 32 | def build_dataset(cfg, default_args=None): 33 | from .dataset_wrappers import (ClassBalancedDataset, ConcatDataset, 34 | KFoldDataset, RepeatDataset) 35 | if isinstance(cfg, (list, tuple)): 36 | dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) 37 | elif cfg['type'] == 'ConcatDataset': 38 | dataset = ConcatDataset( 39 | [build_dataset(c, default_args) for c in cfg['datasets']], 40 | separate_eval=cfg.get('separate_eval', True)) 41 | elif cfg['type'] == 'RepeatDataset': 42 | dataset = RepeatDataset( 43 | build_dataset(cfg['dataset'], default_args), cfg['times']) 44 | elif cfg['type'] == 'ClassBalancedDataset': 45 | dataset = ClassBalancedDataset( 46 | build_dataset(cfg['dataset'], default_args), cfg['oversample_thr']) 47 | elif cfg['type'] == 'KFoldDataset': 48 | cp_cfg = copy.deepcopy(cfg) 49 | if cp_cfg.get('test_mode', None) is None: 50 | cp_cfg['test_mode'] = (default_args or {}).pop('test_mode', False) 51 | cp_cfg['dataset'] = build_dataset(cp_cfg['dataset'], default_args) 52 | cp_cfg.pop('type') 53 | dataset = KFoldDataset(**cp_cfg) 54 | else: 55 | dataset = build_from_cfg(cfg, DATASETS, default_args) 56 | 57 | return dataset 58 | 59 | 60 | def build_dataloader(dataset, 61 | samples_per_gpu, 62 | workers_per_gpu, 63 | num_gpus=1, 64 | dist=True, 65 | shuffle=True, 66 | round_up=True, 67 | seed=None, 68 | pin_memory=True, 69 | persistent_workers=True, 70 | sampler_cfg=None, 71 | **kwargs): 72 | """Build PyTorch DataLoader. 73 | 74 | In distributed training, each GPU/process has a dataloader. 75 | In non-distributed training, there is only one dataloader for all GPUs. 76 | 77 | Args: 78 | dataset (Dataset): A PyTorch dataset. 79 | samples_per_gpu (int): Number of training samples on each GPU, i.e., 80 | batch size of each GPU. 81 | workers_per_gpu (int): How many subprocesses to use for data loading 82 | for each GPU. 83 | num_gpus (int): Number of GPUs. Only used in non-distributed training. 84 | dist (bool): Distributed training/test or not. Default: True. 85 | shuffle (bool): Whether to shuffle the data at every epoch. 86 | Default: True. 87 | round_up (bool): Whether to round up the length of dataset by adding 88 | extra samples to make it evenly divisible. Default: True. 89 | pin_memory (bool): Whether to use pin_memory in DataLoader. 90 | Default: True 91 | persistent_workers (bool): If True, the data loader will not shutdown 92 | the worker processes after a dataset has been consumed once. 93 | This allows to maintain the workers Dataset instances alive. 94 | The argument also has effect in PyTorch>=1.7.0. 95 | Default: True 96 | sampler_cfg (dict): sampler configuration to override the default 97 | sampler 98 | kwargs: any keyword argument to be used to initialize DataLoader 99 | 100 | Returns: 101 | DataLoader: A PyTorch dataloader. 102 | """ 103 | rank, world_size = get_dist_info() 104 | 105 | # Custom sampler logic 106 | if sampler_cfg: 107 | # shuffle=False when val and test 108 | sampler_cfg.update(shuffle=shuffle) 109 | sampler = build_sampler( 110 | sampler_cfg, 111 | default_args=dict( 112 | dataset=dataset, num_replicas=world_size, rank=rank, 113 | seed=seed)) 114 | # Default sampler logic 115 | elif dist: 116 | sampler = build_sampler( 117 | dict( 118 | type='DistributedSampler', 119 | dataset=dataset, 120 | num_replicas=world_size, 121 | rank=rank, 122 | shuffle=shuffle, 123 | round_up=round_up, 124 | seed=seed)) 125 | else: 126 | sampler = None 127 | 128 | # If sampler exists, turn off dataloader shuffle 129 | if sampler is not None: 130 | shuffle = False 131 | 132 | if dist: 133 | batch_size = samples_per_gpu 134 | num_workers = workers_per_gpu 135 | else: 136 | batch_size = num_gpus * samples_per_gpu 137 | num_workers = num_gpus * workers_per_gpu 138 | 139 | init_fn = partial( 140 | worker_init_fn, num_workers=num_workers, rank=rank, 141 | seed=seed) if seed is not None else None 142 | 143 | if digit_version(torch.__version__) >= digit_version('1.8.0'): 144 | kwargs['persistent_workers'] = persistent_workers 145 | if IS_IPU_AVAILABLE: 146 | from mmcv.device.ipu import IPUDataLoader 147 | data_loader = IPUDataLoader( 148 | dataset, 149 | None, 150 | batch_size=samples_per_gpu, 151 | num_workers=num_workers, 152 | shuffle=shuffle, 153 | worker_init_fn=init_fn, 154 | **kwargs) 155 | else: 156 | data_loader = DataLoader( 157 | dataset, 158 | batch_size=batch_size, 159 | sampler=sampler, 160 | num_workers=num_workers, 161 | collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), 162 | pin_memory=pin_memory, 163 | shuffle=shuffle, 164 | worker_init_fn=init_fn, 165 | **kwargs) 166 | 167 | return data_loader 168 | 169 | 170 | def worker_init_fn(worker_id, num_workers, rank, seed): 171 | # The seed of each worker equals to 172 | # num_worker * rank + worker_id + user_seed 173 | worker_seed = num_workers * rank + worker_id + seed 174 | np.random.seed(worker_seed) 175 | random.seed(worker_seed) 176 | torch.manual_seed(worker_seed) 177 | 178 | 179 | def build_sampler(cfg, default_args=None): 180 | if cfg is None: 181 | return None 182 | else: 183 | return build_from_cfg(cfg, SAMPLERS, default_args=default_args) 184 | -------------------------------------------------------------------------------- /ufvl_net/datasets/dataset_wrappers.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import bisect 3 | import math 4 | from collections import defaultdict 5 | 6 | import numpy as np 7 | from mmcv.utils import print_log 8 | from torch.utils.data.dataset import ConcatDataset as _ConcatDataset 9 | 10 | from .builder import DATASETS 11 | 12 | 13 | @DATASETS.register_module() 14 | class ConcatDataset(_ConcatDataset): 15 | """A wrapper of concatenated dataset. 16 | 17 | Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but 18 | add `get_cat_ids` function. 19 | 20 | Args: 21 | datasets (list[:obj:`BaseDataset`]): A list of datasets. 22 | separate_eval (bool): Whether to evaluate the results 23 | separately if it is used as validation dataset. 24 | Defaults to True. 25 | """ 26 | 27 | def __init__(self, datasets, separate_eval=True): 28 | super(ConcatDataset, self).__init__(datasets) 29 | self.separate_eval = separate_eval 30 | 31 | self.CLASSES = datasets[0].CLASSES 32 | 33 | if not separate_eval: 34 | if len(set([type(ds) for ds in datasets])) != 1: 35 | raise NotImplementedError( 36 | 'To evaluate a concat dataset non-separately, ' 37 | 'all the datasets should have same types') 38 | 39 | def get_cat_ids(self, idx): 40 | if idx < 0: 41 | if -idx > len(self): 42 | raise ValueError( 43 | 'absolute value of index should not exceed dataset length') 44 | idx = len(self) + idx 45 | dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) 46 | if dataset_idx == 0: 47 | sample_idx = idx 48 | else: 49 | sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] 50 | return self.datasets[dataset_idx].get_cat_ids(sample_idx) 51 | 52 | def evaluate(self, results, *args, indices=None, logger=None, **kwargs): 53 | """Evaluate the results. 54 | 55 | Args: 56 | results (list[list | tuple]): Testing results of the dataset. 57 | indices (list, optional): The indices of samples corresponding to 58 | the results. It's unavailable on ConcatDataset. 59 | Defaults to None. 60 | logger (logging.Logger | str, optional): Logger used for printing 61 | related information during evaluation. Defaults to None. 62 | 63 | Returns: 64 | dict[str: float]: AP results of the total dataset or each separate 65 | dataset if `self.separate_eval=True`. 66 | """ 67 | if indices is not None: 68 | raise NotImplementedError( 69 | 'Use indices to evaluate speific samples in a ConcatDataset ' 70 | 'is not supported by now.') 71 | 72 | assert len(results) == len(self), \ 73 | ('Dataset and results have different sizes: ' 74 | f'{len(self)} v.s. {len(results)}') 75 | 76 | # Check whether all the datasets support evaluation 77 | for dataset in self.datasets: 78 | assert hasattr(dataset, 'evaluate'), \ 79 | f"{type(dataset)} haven't implemented the evaluate function." 80 | 81 | if self.separate_eval: 82 | total_eval_results = dict() 83 | for dataset_idx, dataset in enumerate(self.datasets): 84 | start_idx = 0 if dataset_idx == 0 else \ 85 | self.cumulative_sizes[dataset_idx-1] 86 | end_idx = self.cumulative_sizes[dataset_idx] 87 | 88 | results_per_dataset = results[start_idx:end_idx] 89 | print_log( 90 | f'Evaluateing dataset-{dataset_idx} with ' 91 | f'{len(results_per_dataset)} images now', 92 | logger=logger) 93 | 94 | eval_results_per_dataset = dataset.evaluate( 95 | results_per_dataset, *args, logger=logger, **kwargs) 96 | for k, v in eval_results_per_dataset.items(): 97 | total_eval_results.update({f'{dataset_idx}_{k}': v}) 98 | 99 | return total_eval_results 100 | else: 101 | original_data_infos = self.datasets[0].data_infos 102 | self.datasets[0].data_infos = sum( 103 | [dataset.data_infos for dataset in self.datasets], []) 104 | eval_results = self.datasets[0].evaluate( 105 | results, logger=logger, **kwargs) 106 | self.datasets[0].data_infos = original_data_infos 107 | return eval_results 108 | 109 | 110 | @DATASETS.register_module() 111 | class RepeatDataset(object): 112 | """A wrapper of repeated dataset. 113 | 114 | The length of repeated dataset will be `times` larger than the original 115 | dataset. This is useful when the data loading time is long but the dataset 116 | is small. Using RepeatDataset can reduce the data loading time between 117 | epochs. 118 | 119 | Args: 120 | dataset (:obj:`BaseDataset`): The dataset to be repeated. 121 | times (int): Repeat times. 122 | """ 123 | 124 | def __init__(self, dataset, times): 125 | self.dataset = dataset 126 | self.times = times 127 | self.CLASSES = dataset.CLASSES 128 | 129 | self._ori_len = len(self.dataset) 130 | 131 | def __getitem__(self, idx): 132 | return self.dataset[idx % self._ori_len] 133 | 134 | def get_cat_ids(self, idx): 135 | return self.dataset.get_cat_ids(idx % self._ori_len) 136 | 137 | def __len__(self): 138 | return self.times * self._ori_len 139 | 140 | def evaluate(self, *args, **kwargs): 141 | raise NotImplementedError( 142 | 'evaluate results on a repeated dataset is weird. ' 143 | 'Please inference and evaluate on the original dataset.') 144 | 145 | def __repr__(self): 146 | """Print the number of instance number.""" 147 | dataset_type = 'Test' if self.test_mode else 'Train' 148 | result = ( 149 | f'\n{self.__class__.__name__} ({self.dataset.__class__.__name__}) ' 150 | f'{dataset_type} dataset with total number of samples {len(self)}.' 151 | ) 152 | return result 153 | 154 | 155 | # Modified from https://github.com/facebookresearch/detectron2/blob/41d475b75a230221e21d9cac5d69655e3415e3a4/detectron2/data/samplers/distributed_sampler.py#L57 # noqa 156 | @DATASETS.register_module() 157 | class ClassBalancedDataset(object): 158 | r"""A wrapper of repeated dataset with repeat factor. 159 | 160 | Suitable for training on class imbalanced datasets like LVIS. Following the 161 | sampling strategy in `this paper`_, in each epoch, an image may appear 162 | multiple times based on its "repeat factor". 163 | 164 | .. _this paper: https://arxiv.org/pdf/1908.03195.pdf 165 | 166 | The repeat factor for an image is a function of the frequency the rarest 167 | category labeled in that image. The "frequency of category c" in [0, 1] 168 | is defined by the fraction of images in the training set (without repeats) 169 | in which category c appears. 170 | 171 | The dataset needs to implement :func:`self.get_cat_ids` to support 172 | ClassBalancedDataset. 173 | 174 | The repeat factor is computed as followed. 175 | 176 | 1. For each category c, compute the fraction :math:`f(c)` of images that 177 | contain it. 178 | 2. For each category c, compute the category-level repeat factor 179 | 180 | .. math:: 181 | r(c) = \max(1, \sqrt{\frac{t}{f(c)}}) 182 | 183 | 3. For each image I and its labels :math:`L(I)`, compute the image-level 184 | repeat factor 185 | 186 | .. math:: 187 | r(I) = \max_{c \in L(I)} r(c) 188 | 189 | Args: 190 | dataset (:obj:`BaseDataset`): The dataset to be repeated. 191 | oversample_thr (float): frequency threshold below which data is 192 | repeated. For categories with ``f_c`` >= ``oversample_thr``, there 193 | is no oversampling. For categories with ``f_c`` < 194 | ``oversample_thr``, the degree of oversampling following the 195 | square-root inverse frequency heuristic above. 196 | """ 197 | 198 | def __init__(self, dataset, oversample_thr): 199 | self.dataset = dataset 200 | self.oversample_thr = oversample_thr 201 | self.CLASSES = dataset.CLASSES 202 | 203 | repeat_factors = self._get_repeat_factors(dataset, oversample_thr) 204 | repeat_indices = [] 205 | for dataset_index, repeat_factor in enumerate(repeat_factors): 206 | repeat_indices.extend([dataset_index] * math.ceil(repeat_factor)) 207 | self.repeat_indices = repeat_indices 208 | 209 | flags = [] 210 | if hasattr(self.dataset, 'flag'): 211 | for flag, repeat_factor in zip(self.dataset.flag, repeat_factors): 212 | flags.extend([flag] * int(math.ceil(repeat_factor))) 213 | assert len(flags) == len(repeat_indices) 214 | self.flag = np.asarray(flags, dtype=np.uint8) 215 | 216 | def _get_repeat_factors(self, dataset, repeat_thr): 217 | # 1. For each category c, compute the fraction # of images 218 | # that contain it: f(c) 219 | category_freq = defaultdict(int) 220 | num_images = len(dataset) 221 | for idx in range(num_images): 222 | cat_ids = set(self.dataset.get_cat_ids(idx)) 223 | for cat_id in cat_ids: 224 | category_freq[cat_id] += 1 225 | for k, v in category_freq.items(): 226 | assert v > 0, f'caterogy {k} does not contain any images' 227 | category_freq[k] = v / num_images 228 | 229 | # 2. For each category c, compute the category-level repeat factor: 230 | # r(c) = max(1, sqrt(t/f(c))) 231 | category_repeat = { 232 | cat_id: max(1.0, math.sqrt(repeat_thr / cat_freq)) 233 | for cat_id, cat_freq in category_freq.items() 234 | } 235 | 236 | # 3. For each image I and its labels L(I), compute the image-level 237 | # repeat factor: 238 | # r(I) = max_{c in L(I)} r(c) 239 | repeat_factors = [] 240 | for idx in range(num_images): 241 | cat_ids = set(self.dataset.get_cat_ids(idx)) 242 | repeat_factor = max( 243 | {category_repeat[cat_id] 244 | for cat_id in cat_ids}) 245 | repeat_factors.append(repeat_factor) 246 | 247 | return repeat_factors 248 | 249 | def __getitem__(self, idx): 250 | ori_index = self.repeat_indices[idx] 251 | return self.dataset[ori_index] 252 | 253 | def __len__(self): 254 | return len(self.repeat_indices) 255 | 256 | def evaluate(self, *args, **kwargs): 257 | raise NotImplementedError( 258 | 'evaluate results on a class-balanced dataset is weird. ' 259 | 'Please inference and evaluate on the original dataset.') 260 | 261 | def __repr__(self): 262 | """Print the number of instance number.""" 263 | dataset_type = 'Test' if self.test_mode else 'Train' 264 | result = ( 265 | f'\n{self.__class__.__name__} ({self.dataset.__class__.__name__}) ' 266 | f'{dataset_type} dataset with total number of samples {len(self)}.' 267 | ) 268 | return result 269 | 270 | 271 | @DATASETS.register_module() 272 | class KFoldDataset: 273 | """A wrapper of dataset for K-Fold cross-validation. 274 | 275 | K-Fold cross-validation divides all the samples in groups of samples, 276 | called folds, of almost equal sizes. And we use k-1 of folds to do training 277 | and use the fold left to do validation. 278 | 279 | Args: 280 | dataset (:obj:`BaseDataset`): The dataset to be divided. 281 | fold (int): The fold used to do validation. Defaults to 0. 282 | num_splits (int): The number of all folds. Defaults to 5. 283 | test_mode (bool): Use the training dataset or validation dataset. 284 | Defaults to False. 285 | seed (int, optional): The seed to shuffle the dataset before splitting. 286 | If None, not shuffle the dataset. Defaults to None. 287 | """ 288 | 289 | def __init__(self, 290 | dataset, 291 | fold=0, 292 | num_splits=5, 293 | test_mode=False, 294 | seed=None): 295 | self.dataset = dataset 296 | self.CLASSES = dataset.CLASSES 297 | self.test_mode = test_mode 298 | self.num_splits = num_splits 299 | 300 | length = len(dataset) 301 | indices = list(range(length)) 302 | if isinstance(seed, int): 303 | rng = np.random.default_rng(seed) 304 | rng.shuffle(indices) 305 | 306 | test_start = length * fold // num_splits 307 | test_end = length * (fold + 1) // num_splits 308 | if test_mode: 309 | self.indices = indices[test_start:test_end] 310 | else: 311 | self.indices = indices[:test_start] + indices[test_end:] 312 | 313 | def get_cat_ids(self, idx): 314 | return self.dataset.get_cat_ids(self.indices[idx]) 315 | 316 | def get_gt_labels(self): 317 | dataset_gt_labels = self.dataset.get_gt_labels() 318 | gt_labels = np.array([dataset_gt_labels[idx] for idx in self.indices]) 319 | return gt_labels 320 | 321 | def __getitem__(self, idx): 322 | return self.dataset[self.indices[idx]] 323 | 324 | def __len__(self): 325 | return len(self.indices) 326 | 327 | def evaluate(self, *args, **kwargs): 328 | kwargs['indices'] = self.indices 329 | return self.dataset.evaluate(*args, **kwargs) 330 | -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .auto_augment import (AutoAugment, AutoContrast, Brightness, 3 | ColorTransform, Contrast, Cutout, Equalize, Invert, 4 | Posterize, RandAugment, Rotate, Sharpness, Shear, 5 | Solarize, SolarizeAdd, Translate) 6 | from .compose import Compose 7 | from .formatting import (Collect, ImageToTensor, ToNumpy, ToPIL, ToTensor, 8 | Transpose, to_tensor) 9 | from .loading import LoadImageFromFile 10 | from .transforms import (CenterCrop, ColorJitter, Lighting, Normalize, Pad, 11 | RandomCrop, RandomErasing, RandomFlip, 12 | RandomGrayscale, RandomResizedCrop, Resize) 13 | 14 | __all__ = [ 15 | 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToPIL', 'ToNumpy', 16 | 'Transpose', 'Collect', 'LoadImageFromFile', 'Resize', 'CenterCrop', 17 | 'RandomFlip', 'Normalize', 'RandomCrop', 'RandomResizedCrop', 18 | 'RandomGrayscale', 'Shear', 'Translate', 'Rotate', 'Invert', 19 | 'ColorTransform', 'Solarize', 'Posterize', 'AutoContrast', 'Equalize', 20 | 'Contrast', 'Brightness', 'Sharpness', 'AutoAugment', 'SolarizeAdd', 21 | 'Cutout', 'RandAugment', 'Lighting', 'ColorJitter', 'RandomErasing', 'Pad' 22 | ] 23 | -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/piplines/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/__pycache__/auto_augment.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/piplines/__pycache__/auto_augment.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/__pycache__/compose.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/piplines/__pycache__/compose.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/__pycache__/formatting.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/piplines/__pycache__/formatting.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/__pycache__/loading.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/piplines/__pycache__/loading.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/__pycache__/transforms.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/datasets/piplines/__pycache__/transforms.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/compose.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from collections.abc import Sequence 3 | 4 | from mmcv.utils import build_from_cfg 5 | 6 | from ..builder import PIPELINES 7 | 8 | 9 | @PIPELINES.register_module() 10 | class Compose(object): 11 | """Compose a data pipeline with a sequence of transforms. 12 | 13 | Args: 14 | transforms (list[dict | callable]): 15 | Either config dicts of transforms or transform objects. 16 | """ 17 | 18 | def __init__(self, transforms): 19 | assert isinstance(transforms, Sequence) 20 | self.transforms = [] 21 | for transform in transforms: 22 | if isinstance(transform, dict): 23 | transform = build_from_cfg(transform, PIPELINES) 24 | self.transforms.append(transform) 25 | elif callable(transform): 26 | self.transforms.append(transform) 27 | else: 28 | raise TypeError('transform must be callable or a dict, but got' 29 | f' {type(transform)}') 30 | 31 | def __call__(self, data): 32 | for t in self.transforms: 33 | data = t(data) 34 | if data is None: 35 | return None 36 | return data 37 | 38 | def __repr__(self): 39 | format_string = self.__class__.__name__ + '(' 40 | for t in self.transforms: 41 | format_string += f'\n {t}' 42 | format_string += '\n)' 43 | return format_string 44 | -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/formatting.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from collections.abc import Sequence 3 | 4 | import mmcv 5 | import numpy as np 6 | import torch 7 | from mmcv.parallel import DataContainer as DC 8 | from PIL import Image 9 | 10 | from ..builder import PIPELINES 11 | 12 | 13 | def to_tensor(data): 14 | """Convert objects of various python types to :obj:`torch.Tensor`. 15 | 16 | Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, 17 | :class:`Sequence`, :class:`int` and :class:`float`. 18 | """ 19 | if isinstance(data, torch.Tensor): 20 | return data 21 | elif isinstance(data, np.ndarray): 22 | return torch.from_numpy(data) 23 | elif isinstance(data, Sequence) and not mmcv.is_str(data): 24 | return torch.tensor(data) 25 | elif isinstance(data, int): 26 | return torch.LongTensor([data]) 27 | elif isinstance(data, float): 28 | return torch.FloatTensor([data]) 29 | else: 30 | raise TypeError( 31 | f'Type {type(data)} cannot be converted to tensor.' 32 | 'Supported types are: `numpy.ndarray`, `torch.Tensor`, ' 33 | '`Sequence`, `int` and `float`') 34 | 35 | 36 | @PIPELINES.register_module() 37 | class ToTensor(object): 38 | 39 | def __init__(self, keys): 40 | self.keys = keys 41 | 42 | def __call__(self, results): 43 | for key in self.keys: 44 | results[key] = to_tensor(results[key]) 45 | return results 46 | 47 | def __repr__(self): 48 | return self.__class__.__name__ + f'(keys={self.keys})' 49 | 50 | 51 | @PIPELINES.register_module() 52 | class ImageToTensor(object): 53 | 54 | def __init__(self, keys): 55 | self.keys = keys 56 | 57 | def __call__(self, results): 58 | for key in self.keys: 59 | img = results[key] 60 | if len(img.shape) < 3: 61 | img = np.expand_dims(img, -1) 62 | results[key] = to_tensor(img.transpose(2, 0, 1)) 63 | return results 64 | 65 | def __repr__(self): 66 | return self.__class__.__name__ + f'(keys={self.keys})' 67 | 68 | 69 | @PIPELINES.register_module() 70 | class Transpose(object): 71 | 72 | def __init__(self, keys, order): 73 | self.keys = keys 74 | self.order = order 75 | 76 | def __call__(self, results): 77 | for key in self.keys: 78 | results[key] = results[key].transpose(self.order) 79 | return results 80 | 81 | def __repr__(self): 82 | return self.__class__.__name__ + \ 83 | f'(keys={self.keys}, order={self.order})' 84 | 85 | 86 | @PIPELINES.register_module() 87 | class ToPIL(object): 88 | 89 | def __init__(self): 90 | pass 91 | 92 | def __call__(self, results): 93 | results['img'] = Image.fromarray(results['img']) 94 | return results 95 | 96 | 97 | @PIPELINES.register_module() 98 | class ToNumpy(object): 99 | 100 | def __init__(self): 101 | pass 102 | 103 | def __call__(self, results): 104 | results['img'] = np.array(results['img'], dtype=np.float32) 105 | return results 106 | 107 | 108 | @PIPELINES.register_module() 109 | class Collect(object): 110 | """Collect data from the loader relevant to the specific task. 111 | 112 | This is usually the last stage of the data loader pipeline. Typically keys 113 | is set to some subset of "img" and "gt_label". 114 | 115 | Args: 116 | keys (Sequence[str]): Keys of results to be collected in ``data``. 117 | meta_keys (Sequence[str], optional): Meta keys to be converted to 118 | ``mmcv.DataContainer`` and collected in ``data[img_metas]``. 119 | Default: ('filename', 'ori_shape', 'img_shape', 'flip', 120 | 'flip_direction', 'img_norm_cfg') 121 | 122 | Returns: 123 | dict: The result dict contains the following keys 124 | 125 | - keys in ``self.keys`` 126 | - ``img_metas`` if available 127 | """ 128 | 129 | def __init__(self, 130 | keys, 131 | meta_keys=('filename', 'ori_filename', 'ori_shape', 132 | 'img_shape', 'flip', 'flip_direction', 133 | 'img_norm_cfg')): 134 | self.keys = keys 135 | self.meta_keys = meta_keys 136 | 137 | def __call__(self, results): 138 | data = {} 139 | img_meta = {} 140 | for key in self.meta_keys: 141 | if key in results: 142 | img_meta[key] = results[key] 143 | data['img_metas'] = DC(img_meta, cpu_only=True) 144 | for key in self.keys: 145 | data[key] = results[key] 146 | return data 147 | 148 | def __repr__(self): 149 | return self.__class__.__name__ + \ 150 | f'(keys={self.keys}, meta_keys={self.meta_keys})' 151 | 152 | 153 | @PIPELINES.register_module() 154 | class WrapFieldsToLists(object): 155 | """Wrap fields of the data dictionary into lists for evaluation. 156 | 157 | This class can be used as a last step of a test or validation 158 | pipeline for single image evaluation or inference. 159 | 160 | Example: 161 | >>> test_pipeline = [ 162 | >>> dict(type='LoadImageFromFile'), 163 | >>> dict(type='Normalize', 164 | mean=[123.675, 116.28, 103.53], 165 | std=[58.395, 57.12, 57.375], 166 | to_rgb=True), 167 | >>> dict(type='ImageToTensor', keys=['img']), 168 | >>> dict(type='Collect', keys=['img']), 169 | >>> dict(type='WrapIntoLists') 170 | >>> ] 171 | """ 172 | 173 | def __call__(self, results): 174 | # Wrap dict fields into lists 175 | for key, val in results.items(): 176 | results[key] = [val] 177 | return results 178 | 179 | def __repr__(self): 180 | return f'{self.__class__.__name__}()' 181 | 182 | 183 | @PIPELINES.register_module() 184 | class ToHalf(object): 185 | 186 | def __init__(self, keys): 187 | self.keys = keys 188 | 189 | def __call__(self, results): 190 | for k in self.keys: 191 | if isinstance(results[k], torch.Tensor): 192 | results[k] = results[k].to(torch.half) 193 | else: 194 | results[k] = results[k].astype(np.float16) 195 | return results 196 | -------------------------------------------------------------------------------- /ufvl_net/datasets/piplines/loading.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import os.path as osp 3 | 4 | import mmcv 5 | import numpy as np 6 | 7 | from ..builder import PIPELINES 8 | 9 | 10 | @PIPELINES.register_module() 11 | class LoadImageFromFile(object): 12 | """Load an image from file. 13 | 14 | Required keys are "img_prefix" and "img_info" (a dict that must contain the 15 | key "filename"). Added or updated keys are "filename", "img", "img_shape", 16 | "ori_shape" (same as `img_shape`) and "img_norm_cfg" (means=0 and stds=1). 17 | 18 | Args: 19 | to_float32 (bool): Whether to convert the loaded image to a float32 20 | numpy array. If set to False, the loaded image is an uint8 array. 21 | Defaults to False. 22 | color_type (str): The flag argument for :func:`mmcv.imfrombytes()`. 23 | Defaults to 'color'. 24 | file_client_args (dict): Arguments to instantiate a FileClient. 25 | See :class:`mmcv.fileio.FileClient` for details. 26 | Defaults to ``dict(backend='disk')``. 27 | """ 28 | 29 | def __init__(self, 30 | to_float32=False, 31 | color_type='color', 32 | file_client_args=dict(backend='disk')): 33 | self.to_float32 = to_float32 34 | self.color_type = color_type 35 | self.file_client_args = file_client_args.copy() 36 | self.file_client = None 37 | 38 | def __call__(self, results): 39 | if self.file_client is None: 40 | self.file_client = mmcv.FileClient(**self.file_client_args) 41 | 42 | if results['img_prefix'] is not None: 43 | filename = osp.join(results['img_prefix'], 44 | results['img_info']['filename']) 45 | else: 46 | filename = results['img_info']['filename'] 47 | 48 | img_bytes = self.file_client.get(filename) 49 | img = mmcv.imfrombytes(img_bytes, flag=self.color_type) 50 | if self.to_float32: 51 | img = img.astype(np.float32) 52 | 53 | results['filename'] = filename 54 | results['ori_filename'] = results['img_info']['filename'] 55 | results['img'] = img 56 | results['img_shape'] = img.shape 57 | results['ori_shape'] = img.shape 58 | num_channels = 1 if len(img.shape) < 3 else img.shape[2] 59 | results['img_norm_cfg'] = dict( 60 | mean=np.zeros(num_channels, dtype=np.float32), 61 | std=np.ones(num_channels, dtype=np.float32), 62 | to_rgb=False) 63 | return results 64 | 65 | def __repr__(self): 66 | repr_str = (f'{self.__class__.__name__}(' 67 | f'to_float32={self.to_float32}, ' 68 | f"color_type='{self.color_type}', " 69 | f'file_client_args={self.file_client_args})') 70 | return repr_str 71 | -------------------------------------------------------------------------------- /ufvl_net/datasets/twelvescenes.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import imgaug 4 | import torch 5 | import numpy as np 6 | import random 7 | from imgaug import augmenters as iaa 8 | import cv2 9 | from torch.utils.data import Dataset 10 | 11 | from .builder import DATASETS 12 | 13 | 14 | @DATASETS.register_module() 15 | class TWESCENES(Dataset): 16 | def __init__(self, root, dataset='7S', scene='heads', split='train', 17 | model='fdanet', aug='False', **kwargs): 18 | self.intrinsics_color = np.array([[572.0, 0.0, 320.0], 19 | [0.0, 572.0, 240.0], 20 | [0.0, 0.0, 1.0]]) 21 | 22 | self.intrinsics_color_inv = np.linalg.inv(self.intrinsics_color) 23 | 24 | self.model = model 25 | self.dataset = dataset 26 | self.aug = aug 27 | self.root = os.path.join(root, '12Scenes') 28 | self.scene = scene 29 | 30 | self.split = split 31 | self.obj_suffixes = ['.color.jpg', '.pose.txt', '.depth.png', 32 | '.label.png'] 33 | self.obj_keys = ['color', 'pose', 'depth', 'label'] 34 | # 这里设定了训练/测试的图片 35 | if self.dataset == '12S' or self.split == 'test': 36 | with open(os.path.join(self.root, self.scene, 37 | '{}{}'.format('test', '.txt')), 'r') as f: 38 | self.frames = f.readlines() 39 | else: 40 | self.frames = [] 41 | with open(os.path.join(self.root, scene, 42 | '{}{}'.format(self.split, '.txt')), 'r') as f: 43 | frames = f.readlines() 44 | frames = [scene + ' ' + frame for frame in frames ] 45 | self.frames.extend(frames) 46 | 47 | def __len__(self): 48 | return len(self.frames) 49 | 50 | def __getitem__(self, index): 51 | # 遍历每一张图片 52 | frame = self.frames[index].rstrip('\n') 53 | seq_id, frame_id = frame.split('-') 54 | objs = {} 55 | objs['color'] = '/mnt/share/sda-2T/xietao/12Scenes/' + self.scene + '/data/' + seq_id + '-' + frame_id + '.color.jpg' 56 | objs['depth'] = '/mnt/share/sda-2T/xietao/12Scenes/' + self.scene + '/data/' + seq_id + '-' + frame_id + '.depth.png' # Twc 57 | objs['pose'] = '/mnt/share/sda-2T/xietao/12Scenes/' + self.scene + '/data/' + seq_id + '-' + frame_id + '.pose.txt' 58 | 59 | img = cv2.imread(objs['color']) 60 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 61 | img = cv2.resize(img, (640, 480)) 62 | 63 | pose = np.loadtxt(objs['pose']) 64 | 65 | if self.split == 'test': 66 | img, pose = to_tensor_query(img, pose) 67 | ret = dict(img=img, gt_lables=pose) 68 | return ret # 返回torch类型的图片和位姿 69 | 70 | depth = cv2.imread(objs['depth'], -1) 71 | pose[0:3, 3] = pose[0:3, 3] * 1000 72 | coord, mask = get_coord(depth, pose, self.intrinsics_color_inv, self.dataset) 73 | img, coord, mask = data_aug(img, coord, mask, self.aug) 74 | 75 | if self.model == 'hscnet': 76 | coord = coord 77 | coord = coord[4::8, 4::8, :] # [60 80] 78 | mask = mask[4::8, 4::8].astype(np.float16) 79 | img, coord, mask = to_tensor(img, coord, mask) 80 | 81 | ret = dict(img=img, gt_lables=coord, mask=mask) 82 | 83 | return ret 84 | 85 | def evaluate(self, results, *args, **kwargs): 86 | transl_err_list = list() 87 | rot_err_list = list() 88 | for i in range(len(results)): 89 | transl_err_list.append(results[i]['trans_error_med']) 90 | rot_err_list.append(results[i]['rot_err_med']) 91 | res_ = np.array([transl_err_list, rot_err_list]).T 92 | 93 | return dict(median_trans_error=np.median(res_[:, 0]), 94 | median_rot_error=np.median(res_[:, 1]), 95 | accuracy=np.sum((res_[:, 0] <= 0.050) * (res_[:, 1] <= 5)) * 1. / len(res_) 96 | ) 97 | 98 | 99 | # depth:深度图[480 640] 100 | # 返回深度图对齐到RGB图后,RGB相应的深度信息 101 | def get_depth(depth, calibration_extrinsics, intrinsics_color, 102 | intrinsics_depth_inv): 103 | """Return the calibrated depth image (7-Scenes). 104 | Calibration parameters from DSAC (https://github.com/cvlab-dresden/DSAC) 105 | are used. 106 | """ 107 | ''' 108 | 利用深度摄像头内参矩阵把深度平面坐标(深度图坐标)转换到深度摄像头空间坐标, 109 | 再利用外参计算旋转矩阵和平移矩阵,把深度摄像头空间坐标转换到RGB摄像头空间坐标, 110 | 最后利用RGB摄像头内参矩阵把RGB摄像头空间坐标转换到RGB平面坐标(RGB图坐标)。 111 | 这里只记录一下最终测试程序的思路: 112 | ''' 113 | img_height, img_width = depth.shape[0], depth.shape[1] 114 | depth_ = np.zeros_like(depth) # [480 640] 115 | x = np.linspace(0, img_width - 1, img_width) # 640 116 | y = np.linspace(0, img_height - 1, img_height) # 480 117 | 118 | xx, yy = np.meshgrid(x, y) # 坐标网格化[img_width img_height] 119 | xx = np.reshape(xx, (1, -1)) # [1, img_width*img_height] 120 | yy = np.reshape(yy, (1, -1)) # [1, img_width*img_height] 121 | ones = np.ones_like(xx) # [1, img_width*img_height] 122 | 123 | pcoord_depth = np.concatenate((xx, yy, ones), axis=0) # [3, img_width*img_height], 像素坐标 124 | depth = np.reshape(depth, (1, img_height * img_width)) # [1, img_width*img_height] 125 | 126 | ccoord_depth = np.dot(intrinsics_depth_inv, pcoord_depth) * depth # 像素坐标-->归一化坐标-->相机坐标[3, img_width*img_height] 127 | 128 | ccoord_depth[1, :] = - ccoord_depth[1, :] 129 | ccoord_depth[2, :] = - ccoord_depth[2, :] 130 | 131 | ccoord_depth = np.concatenate((ccoord_depth, ones), axis=0) # [4, img_width*img_height] 132 | ccoord_color = np.dot(calibration_extrinsics, ccoord_depth) # [3, img_width*img_height],RGB相机坐标 133 | 134 | ccoord_color = ccoord_color[0:3, :] 135 | ccoord_color[1, :] = - ccoord_color[1, :] 136 | ccoord_color[2, :] = depth 137 | 138 | pcoord_color = np.dot(intrinsics_color, ccoord_color) # RGB像素坐标*Z 139 | pcoord_color = pcoord_color[:, pcoord_color[2, :] != 0] 140 | 141 | pcoord_color[0, :] = pcoord_color[0, :] / pcoord_color[2, :] + 0.5 # RGB像素坐标 142 | pcoord_color[0, :] = pcoord_color[0, :].astype(int) 143 | pcoord_color[1, :] = pcoord_color[1, :] / pcoord_color[2, :] + 0.5 144 | pcoord_color[1, :] = pcoord_color[1, :].astype(int) 145 | pcoord_color = pcoord_color[:, pcoord_color[0, :] >= 0] 146 | pcoord_color = pcoord_color[:, pcoord_color[1, :] >= 0] 147 | 148 | pcoord_color = pcoord_color[:, pcoord_color[0, :] < img_width] 149 | pcoord_color = pcoord_color[:, pcoord_color[1, :] < img_height] 150 | 151 | depth_[pcoord_color[1, :].astype(int), 152 | pcoord_color[0, :].astype(int)] = pcoord_color[2, :] 153 | return depth_ 154 | 155 | 156 | def get_coord(depth, pose, intrinsics_color_inv, dataset): 157 | """Generate the ground truth scene coordinates from depth and pose. 158 | """ 159 | img_height, img_width = depth.shape[0], depth.shape[1] # 480 640 160 | mask = np.ones_like(depth) 161 | mask[depth == 0] = 0 # 深度为0处,数值为0,否则为1 162 | mask = np.reshape(mask, (img_height, img_width, 1)) # [480 640 1] 163 | x = np.linspace(0, img_width - 1, img_width) 164 | y = np.linspace(0, img_height - 1, img_height) 165 | 166 | xx, yy = np.meshgrid(x, y) 167 | 168 | xx = np.reshape(xx, (1, -1)) # [1, 640*480] 169 | yy = np.reshape(yy, (1, -1)) # [1, 640*480] 170 | ones = np.ones_like(xx) # [1, 640*480] 171 | pcoord = np.concatenate((xx, yy, ones), axis=0) # [3, 640*480],像素坐标 172 | 173 | depth = np.reshape(depth, (1, img_height * img_width)) # [1, 640*480] 174 | ccoord = np.dot(intrinsics_color_inv, pcoord) * depth # 相机坐标 [3 640*480] 175 | ccoord = np.concatenate((ccoord, ones), axis=0) # 相机坐标 [4 640*480] 176 | 177 | # if dataset == 'my': 178 | # scoord = np.dot(np.swapaxes(ccoord,0,1), pose) 179 | # else: 180 | scoord = np.dot(pose, ccoord) # 世界坐标 [3 640*480] 181 | scoord = np.swapaxes(scoord, 0, 1) # 世界坐标 [640*480 3] 182 | 183 | scoord = scoord[:, 0:3] 184 | scoord = np.reshape(scoord, (img_height, img_width, 3)) # 世界坐标 [480 640 3] 185 | scoord = scoord * mask 186 | mask = np.reshape(mask, (img_height, img_width)) # [480 640] 187 | 188 | return scoord, mask 189 | 190 | 191 | # 数据增强操作 192 | def data_aug(img, coord, mask, aug=True, sp_coords=None): 193 | img_h, img_w = img.shape[0:2] 194 | if aug: 195 | trans_x = random.uniform(-0.2, 0.2) # 平移 196 | trans_y = random.uniform(-0.2, 0.2) 197 | 198 | aug_add = iaa.Add(random.randint(-20, 20)) 199 | 200 | scale = random.uniform(0.7, 1.5) # 缩放 201 | rotate = random.uniform(-30, 30) # 旋转 202 | shear = random.uniform(-10, 10) # 裁剪 203 | 204 | aug_affine = iaa.Affine(scale=scale, rotate=rotate, 205 | shear=shear, translate_percent={"x": trans_x, "y": trans_y}) 206 | aug_affine_lbl = iaa.Affine(scale=scale, rotate=rotate, 207 | shear=shear, translate_percent={"x": trans_x, "y": trans_y}, 208 | order=0, cval=1) 209 | img = aug_add.augment_image(img) 210 | else: 211 | trans_x = random.randint(-3, 4) 212 | trans_y = random.randint(-3, 4) 213 | 214 | aug_affine = iaa.Affine(translate_px={"x": trans_x, "y": trans_y}) 215 | 216 | padding = torch.randint(0, 255, size=(img_h, img_w, 3)).data.numpy().astype(np.uint8) 217 | padding_mask = np.ones((img_h, img_w)).astype(np.uint8) 218 | 219 | img = aug_affine.augment_image(img) 220 | coord = aug_affine.augment_image(coord) 221 | mask = aug_affine.augment_image(mask) 222 | mask = np.round(mask) 223 | padding_mask = aug_affine.augment_image(padding_mask) 224 | img = img + (1 - np.expand_dims(padding_mask, axis=2)) * padding 225 | 226 | if isinstance(sp_coords, np.ndarray): 227 | ia_kpts = [] 228 | out_kpts = [] 229 | for i in range(sp_coords.shape[0]): 230 | # if np.isnan(sp_coords[i][0]): 231 | # ia_kpts.append(imgaug.Keypoint(x=0, y=0)) 232 | ia_kpts.append(imgaug.Keypoint(x=sp_coords[i][0], y=sp_coords[i][1])) 233 | ia_kpts = imgaug.KeypointsOnImage(ia_kpts, shape=img.shape) 234 | ia_kpts = aug_affine_lbl.augment_keypoints(ia_kpts) 235 | for i in range(len(ia_kpts)): 236 | out_kpts.append(np.array((ia_kpts[i].x, ia_kpts[i].y))) 237 | out_kpts = np.stack(out_kpts, axis=0) 238 | return img, coord, mask, out_kpts 239 | else: 240 | return img, coord, mask 241 | 242 | 243 | # img [480 640 3] 244 | # coord_img [60, 80, 3] 245 | # mask [60 80] 246 | def to_tensor(img, coord_img, mask): 247 | img = img.transpose(2, 0, 1) 248 | coord_img = coord_img.transpose(2, 0, 1) 249 | img = img / 255. 250 | img = img * 2. - 1. 251 | coord_img = coord_img / 1000. 252 | img = torch.from_numpy(img).float() 253 | coord_img = torch.from_numpy(coord_img).float() 254 | mask = torch.from_numpy(mask).float() 255 | return img, coord_img, mask 256 | 257 | 258 | def to_tensor_query(img, pose): 259 | img = img.transpose(2, 0, 1) 260 | img = img / 255. 261 | img = img * 2. - 1. 262 | img = torch.from_numpy(img).float() 263 | pose = torch.from_numpy(pose).float() 264 | return img, pose 265 | 266 | 267 | def data_aug_label(img, coord, mask, lbl, aug=True): 268 | img_h, img_w = img.shape[0:2] 269 | if aug: 270 | trans_x = random.uniform(-0.2, 0.2) 271 | trans_y = random.uniform(-0.2, 0.2) 272 | 273 | aug_add = iaa.Add(random.randint(-20, 20)) 274 | 275 | scale = random.uniform(0.7, 1.5) 276 | rotate = random.uniform(-30, 30) 277 | shear = random.uniform(-10, 10) 278 | 279 | aug_affine = iaa.Affine(scale=scale, rotate=rotate, 280 | shear=shear, translate_percent={"x": trans_x, "y": trans_y}) 281 | aug_affine_lbl = iaa.Affine(scale=scale, rotate=rotate, 282 | shear=shear, translate_percent={"x": trans_x, "y": trans_y}, 283 | order=0, cval=1) 284 | img = aug_add.augment_image(img) 285 | else: 286 | trans_x = random.randint(-3, 4) 287 | trans_y = random.randint(-3, 4) 288 | 289 | aug_affine = iaa.Affine(translate_px={"x": trans_x, "y": trans_y}) 290 | aug_affine_lbl = iaa.Affine(translate_px={"x": trans_x, "y": trans_y}, 291 | order=0, cval=1) 292 | 293 | padding = torch.randint(0, 255, size=(img_h, 294 | img_w, 3)).data.numpy().astype(np.uint8) 295 | padding_mask = np.ones((img_h, img_w)).astype(np.uint8) 296 | 297 | img = aug_affine.augment_image(img) 298 | coord = aug_affine.augment_image(coord) 299 | mask = aug_affine.augment_image(mask) 300 | mask = np.round(mask) 301 | lbl = aug_affine_lbl.augment_image(lbl) 302 | padding_mask = aug_affine.augment_image(padding_mask) 303 | img = img + (1 - np.expand_dims(padding_mask, axis=2)) * padding 304 | 305 | return img, coord, mask, lbl 306 | 307 | 308 | def one_hot(x, N=25): 309 | one_hot = torch.FloatTensor(N, x.size(0), x.size(1)).zero_() 310 | one_hot = one_hot.scatter_(0, x.unsqueeze(0), 1) 311 | return one_hot 312 | 313 | 314 | def to_tensor_label(img, coord_img, mask, lbl, N1=25, N2=25): 315 | img = img.transpose(2, 0, 1) 316 | coord_img = coord_img.transpose(2, 0, 1) 317 | 318 | img = img / 255. 319 | img = img * 2. - 1. 320 | 321 | coord_img = coord_img / 1000. 322 | 323 | img = torch.from_numpy(img).float() 324 | coord_img = torch.from_numpy(coord_img).float() 325 | mask = torch.from_numpy(mask).float() 326 | 327 | lbl = torch.from_numpy(lbl / 1.0).long() 328 | lbl_oh = one_hot(lbl, N=N1) 329 | return img, coord_img, mask, lbl, lbl_oh 330 | 331 | 332 | def to_tensor_query_label(img, pose): 333 | img = img.transpose(2, 0, 1) 334 | img = img / 255. 335 | img = img * 2. - 1. 336 | img = torch.from_numpy(img).float() 337 | pose = torch.from_numpy(pose).float() 338 | 339 | return img, pose 340 | -------------------------------------------------------------------------------- /ufvl_net/models/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .backbones import * # noqa: F401,F403 3 | from .builder import (BACKBONES, ARCHITECTURE, HEADS, 4 | build_backbone, build_architecture, build_head) 5 | from .architecture import * # noqa: F401,F403 6 | from .heads import * # noqa: F401,F403 7 | 8 | __all__ = [ 9 | 'BACKBONES', 'HEADS', 'ARCHITECTURE', 'build_backbone', 10 | 'build_head', 'build_architecture' 11 | ] 12 | -------------------------------------------------------------------------------- /ufvl_net/models/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/__pycache__/builder.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/__pycache__/builder.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/architecture/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .vis_loc import FDANET 3 | 4 | __all__ = ['FDANET'] 5 | -------------------------------------------------------------------------------- /ufvl_net/models/architecture/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/architecture/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/architecture/__pycache__/base.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/architecture/__pycache__/base.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/architecture/__pycache__/vis_loc.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/architecture/__pycache__/vis_loc.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/architecture/base.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from abc import ABCMeta, abstractmethod 3 | from collections import OrderedDict 4 | from typing import Sequence 5 | 6 | import mmcv 7 | import torch 8 | import torch.distributed as dist 9 | from mmcv.runner import BaseModule, auto_fp16 10 | 11 | class BaseClassifier(BaseModule, metaclass=ABCMeta): 12 | """Base class for classifiers.""" 13 | 14 | def __init__(self, init_cfg=None): 15 | super(BaseClassifier, self).__init__(init_cfg) 16 | self.fp16_enabled = False 17 | 18 | @property 19 | def with_neck(self): 20 | return hasattr(self, 'neck') and self.neck is not None 21 | 22 | @property 23 | def with_head(self): 24 | return hasattr(self, 'head') and self.head is not None 25 | 26 | @abstractmethod 27 | def extract_feat(self, imgs, stage=None): 28 | pass 29 | 30 | def extract_feats(self, imgs, stage=None): 31 | assert isinstance(imgs, Sequence) 32 | kwargs = {} if stage is None else {'stage': stage} 33 | for img in imgs: 34 | yield self.extract_feat(img, **kwargs) 35 | 36 | @abstractmethod 37 | def forward_train(self, imgs, **kwargs): 38 | """ 39 | Args: 40 | img (list[Tensor]): List of tensors of shape (1, C, H, W). 41 | Typically these should be mean centered and std scaled. 42 | kwargs (keyword arguments): Specific to concrete implementation. 43 | """ 44 | pass 45 | 46 | @abstractmethod 47 | def simple_test(self, img, **kwargs): 48 | pass 49 | 50 | def forward_test(self, imgs, **kwargs): 51 | """ 52 | Args: 53 | imgs (List[Tensor]): the outer list indicates test-time 54 | augmentations and inner Tensor should have a shape NxCxHxW, 55 | which contains all images in the batch. 56 | """ 57 | if isinstance(imgs, torch.Tensor): 58 | imgs = [imgs] 59 | for var, name in [(imgs, 'imgs')]: 60 | if not isinstance(var, list): 61 | raise TypeError(f'{name} must be a list, but got {type(var)}') 62 | 63 | if len(imgs) == 1: 64 | return self.simple_test(imgs[0], **kwargs) 65 | else: 66 | raise NotImplementedError('aug_test has not been implemented') 67 | 68 | @auto_fp16(apply_to=('img', )) 69 | def forward(self, img, return_loss=True, **kwargs): 70 | """Calls either forward_train or forward_test depending on whether 71 | return_loss=True. 72 | 73 | Note this setting will change the expected inputs. When 74 | `return_loss=True`, img and img_meta are single-nested (i.e. Tensor and 75 | List[dict]), and when `resturn_loss=False`, img and img_meta should be 76 | double nested (i.e. List[Tensor], List[List[dict]]), with the outer 77 | list indicating test time augmentations. 78 | """ 79 | if return_loss: 80 | return self.forward_train(img, **kwargs) 81 | else: 82 | return self.forward_test(img, **kwargs) 83 | 84 | def _parse_losses(self, losses): 85 | log_vars = OrderedDict() 86 | for loss_name, loss_value in losses.items(): 87 | if isinstance(loss_value, torch.Tensor): 88 | log_vars[loss_name] = loss_value.mean() 89 | elif isinstance(loss_value, list): 90 | log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) 91 | elif isinstance(loss_value, dict): 92 | for name, value in loss_value.items(): 93 | log_vars[name] = value 94 | else: 95 | raise TypeError( 96 | f'{loss_name} is not a tensor or list of tensors') 97 | 98 | loss = sum(_value for _key, _value in log_vars.items() 99 | if 'loss' in _key) 100 | 101 | log_vars['loss'] = loss 102 | for loss_name, loss_value in log_vars.items(): 103 | # reduce loss when distributed training 104 | if dist.is_available() and dist.is_initialized(): 105 | loss_value = loss_value.data.clone() 106 | dist.all_reduce(loss_value.div_(dist.get_world_size())) 107 | log_vars[loss_name] = loss_value.item() 108 | 109 | return loss, log_vars 110 | 111 | def train_step(self, data, optimizer=None, **kwargs): 112 | """The iteration step during training. 113 | 114 | This method defines an iteration step during training, except for the 115 | back propagation and optimizer updating, which are done in an optimizer 116 | hook. Note that in some complicated cases or models, the whole process 117 | including back propagation and optimizer updating are also defined in 118 | this method, such as GAN. 119 | 120 | Args: 121 | data (dict): The output of dataloader. 122 | optimizer (:obj:`torch.optim.Optimizer` | dict, optional): The 123 | optimizer of runner is passed to ``train_step()``. This 124 | argument is unused and reserved. 125 | 126 | Returns: 127 | dict: Dict of outputs. The following fields are contained. 128 | - loss (torch.Tensor): A tensor for back propagation, which \ 129 | can be a weighted sum of multiple losses. 130 | - log_vars (dict): Dict contains all the variables to be sent \ 131 | to the logger. 132 | - num_samples (int): Indicates the batch size (when the model \ 133 | is DDP, it means the batch size on each GPU), which is \ 134 | used for averaging the logs. 135 | """ 136 | losses = self(**data) 137 | loss, log_vars = self._parse_losses(losses) 138 | 139 | outputs = dict( 140 | loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) 141 | 142 | return outputs 143 | 144 | def val_step(self, data, optimizer=None, **kwargs): 145 | """The iteration step during validation. 146 | 147 | This method shares the same signature as :func:`train_step`, but used 148 | during val epochs. Note that the evaluation after training epochs is 149 | not implemented with this method, but an evaluation hook. 150 | 151 | Args: 152 | data (dict): The output of dataloader. 153 | optimizer (:obj:`torch.optim.Optimizer` | dict, optional): The 154 | optimizer of runner is passed to ``train_step()``. This 155 | argument is unused and reserved. 156 | 157 | Returns: 158 | dict: Dict of outputs. The following fields are contained. 159 | - loss (torch.Tensor): A tensor for back propagation, which \ 160 | can be a weighted sum of multiple losses. 161 | - log_vars (dict): Dict contains all the variables to be sent \ 162 | to the logger. 163 | - num_samples (int): Indicates the batch size (when the model \ 164 | is DDP, it means the batch size on each GPU), which is \ 165 | used for averaging the logs. 166 | """ 167 | losses = self(**data) 168 | loss, log_vars = self._parse_losses(losses) 169 | 170 | outputs = dict( 171 | loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) 172 | 173 | return outputs 174 | 175 | -------------------------------------------------------------------------------- /ufvl_net/models/architecture/pnpransac/build/temp.linux-x86_64-cpython-37/pnpransacpy.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/architecture/pnpransac/build/temp.linux-x86_64-cpython-37/pnpransacpy.o -------------------------------------------------------------------------------- /ufvl_net/models/architecture/pnpransac/pnpransac.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PnP-RANSAC implementation based on DSAC++ 3 | Code: https://github.com/vislearn/LessMore 4 | Paper: https://arxiv.org/abs/1711.10228 5 | */ 6 | 7 | /* 8 | Copyright (c) 2016, TU Dresden 9 | Copyright (c) 2017, Heidelberg University 10 | All rights reserved. 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are met: 13 | * Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in the 17 | documentation and/or other materials provided with the distribution. 18 | * Neither the name of the TU Dresden, Heidelberg University nor the 19 | names of its contributors may be used to endorse or promote products 20 | derived from this software without specific prior written permission. 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL TU DRESDEN OR HEIDELBERG UNIVERSITY BE LIABLE FOR ANY 25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include "pnpransac.h" 34 | 35 | std::vector ThreadRand::generators; 36 | bool ThreadRand::initialised = false; 37 | 38 | void ThreadRand::forceInit(unsigned seed) 39 | { 40 | initialised = false; 41 | init(seed); 42 | } 43 | 44 | void ThreadRand::init(unsigned seed) 45 | { 46 | #pragma omp critical 47 | { 48 | if(!initialised) 49 | { 50 | unsigned nThreads = omp_get_max_threads(); 51 | 52 | for(unsigned i = 0; i < nThreads; i++) 53 | { 54 | generators.push_back(std::mt19937()); 55 | generators[i].seed(i+seed); 56 | } 57 | 58 | initialised = true; 59 | } 60 | } 61 | } 62 | 63 | int ThreadRand::irand(int min, int max, int tid) 64 | { 65 | std::uniform_int_distribution dist(min, max); 66 | 67 | unsigned threadID = omp_get_thread_num(); 68 | if(tid >= 0) threadID = tid; 69 | 70 | if(!initialised) init(); 71 | 72 | return dist(ThreadRand::generators[threadID]); 73 | } 74 | 75 | double ThreadRand::drand(double min, double max, int tid) 76 | { 77 | std::uniform_real_distribution dist(min, max); 78 | 79 | unsigned threadID = omp_get_thread_num(); 80 | if(tid >= 0) threadID = tid; 81 | 82 | if(!initialised) init(); 83 | 84 | return dist(ThreadRand::generators[threadID]); 85 | } 86 | 87 | double ThreadRand::dgauss(double mean, double stdDev, int tid) 88 | { 89 | std::normal_distribution dist(mean, stdDev); 90 | 91 | unsigned threadID = omp_get_thread_num(); 92 | if(tid >= 0) threadID = tid; 93 | 94 | if(!initialised) init(); 95 | 96 | return dist(ThreadRand::generators[threadID]); 97 | } 98 | 99 | int irand(int incMin, int excMax, int tid) 100 | { 101 | return ThreadRand::irand(incMin, excMax - 1, tid); 102 | } 103 | 104 | double drand(double incMin, double incMax,int tid) 105 | { 106 | return ThreadRand::drand(incMin, incMax, tid); 107 | } 108 | 109 | int igauss(int mean, int stdDev, int tid) 110 | { 111 | return (int) ThreadRand::dgauss(mean, stdDev, tid); 112 | } 113 | 114 | double dgauss(double mean, double stdDev, int tid) 115 | { 116 | return ThreadRand::dgauss(mean, stdDev, tid); 117 | } 118 | 119 | namespace poseSolver { 120 | 121 | std::pair getInvHyp(const std::pair& hyp) 122 | { 123 | cv::Mat_ hypR, trans = cv::Mat_::eye(4, 4); 124 | cv::Rodrigues(hyp.first, hypR); 125 | 126 | hypR.copyTo(trans.rowRange(0,3).colRange(0,3)); 127 | trans(0, 3) = hyp.second.at(0, 0); 128 | trans(1, 3) = hyp.second.at(0, 1); 129 | trans(2, 3) = hyp.second.at(0, 2); 130 | 131 | trans = trans.inv(); 132 | 133 | std::pair invHyp; 134 | cv::Rodrigues(trans.rowRange(0,3).colRange(0,3), invHyp.first); 135 | invHyp.second = cv::Mat_(1, 3); 136 | invHyp.second.at(0, 0) = trans(0, 3); 137 | invHyp.second.at(0, 1) = trans(1, 3); 138 | invHyp.second.at(0, 2) = trans(2, 3); 139 | 140 | return invHyp; 141 | } 142 | 143 | double calcAngularDistance(const std::pair & h1, const std::pair & h2) 144 | { 145 | cv::Mat r1, r2; 146 | cv::Rodrigues(h1.first, r1); 147 | cv::Rodrigues(h2.first, r2); 148 | 149 | cv::Mat rotDiff= r2 * r1.t(); 150 | double trace = cv::trace(rotDiff)[0]; 151 | 152 | trace = std::min(3.0, std::max(-1.0, trace)); 153 | return 180*acos((trace-1.0)/2.0)/CV_PI; 154 | } 155 | 156 | double maxLoss(const std::pair & h1, const std::pair & h2) 157 | { 158 | // measure loss of inverted poses (camera pose instead of scene pose) 159 | std::pair invH1 = getInvHyp(h1); 160 | std::pair invH2 = getInvHyp(h2); 161 | 162 | double rotErr = calcAngularDistance(invH1, invH2); 163 | double tErr = cv::norm(invH1.second - invH2.second); 164 | 165 | return std::max(rotErr, tErr * 100); 166 | } 167 | 168 | inline bool safeSolvePnP( 169 | const std::vector& objPts, 170 | const std::vector& imgPts, 171 | const cv::Mat& camMat, 172 | const cv::Mat& distCoeffs, 173 | cv::Mat& rot, 174 | cv::Mat& trans, 175 | bool extrinsicGuess, 176 | int methodFlag) 177 | { 178 | if(rot.type() == 0) rot = cv::Mat_::zeros(1, 3); 179 | if(trans.type() == 0) trans= cv::Mat_::zeros(1, 3); 180 | 181 | if(!cv::solvePnP(objPts, imgPts, camMat, distCoeffs, rot, trans, extrinsicGuess,methodFlag)) 182 | { 183 | rot = cv::Mat_::zeros(1, 3); 184 | trans = cv::Mat_::zeros(1, 3); 185 | return false; 186 | } 187 | return true; 188 | } 189 | 190 | PnPRANSAC::PnPRANSAC () { 191 | this->camMat = cv::Mat_::eye(3, 3); 192 | } 193 | 194 | PnPRANSAC::PnPRANSAC (float fx, float fy, float cx, float cy) { 195 | this->camMat = cv::Mat_::eye(3, 3); 196 | this->camMat(0,0) = fx; 197 | this->camMat(1,1) = fy; 198 | this->camMat(0,2) = cx; 199 | this->camMat(1,2) = cy; 200 | } 201 | 202 | PnPRANSAC::~PnPRANSAC () {} 203 | 204 | void PnPRANSAC::camMatUpdate(float fx, float fy, float cx, float cy){ 205 | this->camMat = cv::Mat_::eye(3, 3); 206 | this->camMat(0,0) = fx; 207 | this->camMat(1,1) = fy; 208 | this->camMat(0,2) = cx; 209 | this->camMat(1,2) = cy; 210 | } 211 | 212 | double* PnPRANSAC::RANSACLoop( 213 | float* imgPts_, 214 | float* objPts_, 215 | int nPts, 216 | int objHyps) 217 | { 218 | int inlierThreshold2D = 10; 219 | int refSteps = 100; 220 | 221 | std::vector imgPts(nPts); 222 | std::vector objPts(nPts); 223 | #pragma omp parallel for 224 | for(unsigned i=0; i> sampledImgPts(objHyps); 231 | std::vector> sampledObjPts(objHyps); 232 | std::vector> rotHyp(objHyps); 233 | std::vector> tHyp(objHyps); 234 | std::vector scores(objHyps); 235 | std::vector> reproDiff(objHyps); 236 | 237 | // sample hypotheses 238 | #pragma omp parallel for 239 | for(int h = 0; h < objHyps; h++) 240 | while(true) 241 | { 242 | std::vector projections; 243 | std::vector alreadyChosen(nPts,0); 244 | sampledImgPts[h].clear(); 245 | sampledObjPts[h].clear(); 246 | 247 | for(int j = 0; j < 4; j++) 248 | { 249 | int idx = irand(0, nPts); 250 | 251 | if(alreadyChosen[idx] > 0) 252 | { 253 | j--; 254 | continue; 255 | } 256 | 257 | alreadyChosen[idx] = 1; 258 | 259 | sampledImgPts[h].push_back(imgPts[idx]); // 2D location in the original RGB image 260 | sampledObjPts[h].push_back(objPts[idx]); // 3D object coordinate 261 | 262 | } 263 | 264 | if(!safeSolvePnP(sampledObjPts[h], sampledImgPts[h], this->camMat, cv::Mat(), rotHyp[h], tHyp[h], false, CV_P3P)) 265 | { 266 | continue; 267 | 268 | } 269 | 270 | cv::projectPoints(sampledObjPts[h], rotHyp[h], tHyp[h], this->camMat, cv::Mat(), projections); 271 | 272 | // check reconstruction, 4 sampled points should be reconstructed perfectly 273 | bool foundOutlier = false; 274 | for(unsigned j = 0; j < sampledImgPts[h].size(); j++) 275 | { 276 | if(cv::norm(sampledImgPts[h][j] - projections[j]) < inlierThreshold2D) 277 | continue; 278 | foundOutlier = true; 279 | break; 280 | } 281 | if(foundOutlier) 282 | continue; 283 | else{ 284 | // compute reprojection error and hypothesis score 285 | std::vector projections; 286 | cv::projectPoints(objPts, rotHyp[h], tHyp[h], this->camMat, cv::Mat(), projections); 287 | std::vector diff(nPts); 288 | float score = 0.; 289 | for(unsigned pt = 0; pt < imgPts.size(); pt++) 290 | { 291 | float err = cv::norm(imgPts[pt] - projections[pt]); 292 | diff[pt] = err; 293 | score = score + (1. / (1. + std::exp(-(0.5*(err-inlierThreshold2D))))); 294 | } 295 | reproDiff[h] = diff; 296 | scores[h] = score; 297 | break; 298 | } 299 | } 300 | 301 | int hypIdx = std::min_element(scores.begin(),scores.end()) - scores.begin(); // select winning hypothesis 302 | 303 | double convergenceThresh = 0.01; // stop refinement if 6D pose vector converges 304 | 305 | std::vector localDiff = reproDiff[hypIdx]; 306 | 307 | for(int rStep = 0; rStep < refSteps; rStep++) 308 | { 309 | // collect inliers 310 | std::vector localImgPts; 311 | std::vector localObjPts; 312 | 313 | for(int pt = 0; pt < nPts; pt++) 314 | { 315 | if(localDiff[pt] < inlierThreshold2D) 316 | { 317 | localImgPts.push_back(imgPts[pt]); 318 | localObjPts.push_back(objPts[pt]); 319 | } 320 | } 321 | 322 | if(localImgPts.size() < 4) 323 | break; 324 | 325 | // recalculate pose 326 | cv::Mat_ rotNew = rotHyp[hypIdx].clone(); 327 | cv::Mat_ tNew = tHyp[hypIdx].clone(); 328 | 329 | if(!safeSolvePnP(localObjPts, localImgPts, this->camMat, cv::Mat(), rotNew, tNew, true, (localImgPts.size() > 4) ? CV_ITERATIVE : CV_P3P)) 330 | break; //abort if PnP fails 331 | std::pair hypNew; 332 | std::pair hypOld; 333 | hypNew.first = rotNew; 334 | hypNew.second = tNew; 335 | 336 | hypOld.first = rotHyp[hypIdx]; 337 | hypOld.second = tHyp[hypIdx]; 338 | if(maxLoss(hypNew, hypOld) < convergenceThresh) 339 | break; // convergned 340 | 341 | rotHyp[hypIdx] = rotNew; 342 | tHyp[hypIdx] = tNew; 343 | 344 | // recalculate pose errors 345 | std::vector projections; 346 | cv::projectPoints(objPts, rotHyp[hypIdx], tHyp[hypIdx], this->camMat, cv::Mat(), projections); 347 | std::vector diff(nPts); 348 | 349 | #pragma omp parallel for 350 | for(unsigned pt = 0; pt < imgPts.size(); pt++) 351 | { 352 | float err = cv::norm(imgPts[pt] - projections[pt]); 353 | diff[pt] = err; 354 | } 355 | localDiff = diff; 356 | } 357 | 358 | static double pose[6]; 359 | for (int i = 0; i < 3; i++) 360 | pose[i] = rotHyp[hypIdx](0,i); 361 | for (int i = 3; i < 6; i++) 362 | pose[i] = tHyp[hypIdx](0,i-3); 363 | return pose; 364 | } 365 | } 366 | 367 | -------------------------------------------------------------------------------- /ufvl_net/models/architecture/pnpransac/pnpransac.cpython-37m-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/architecture/pnpransac/pnpransac.cpython-37m-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /ufvl_net/models/architecture/pnpransac/pnpransac.h: -------------------------------------------------------------------------------- 1 | /* 2 | PnP-RANSAC implementation based on DSAC++ 3 | Code: https://github.com/vislearn/LessMore 4 | Paper: https://arxiv.org/abs/1711.10228 5 | */ 6 | 7 | /* 8 | Copyright (c) 2016, TU Dresden 9 | Copyright (c) 2017, Heidelberg University 10 | All rights reserved. 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are met: 13 | * Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in the 17 | documentation and/or other materials provided with the distribution. 18 | * Neither the name of the TU Dresden, Heidelberg University nor the 19 | names of its contributors may be used to endorse or promote products 20 | derived from this software without specific prior written permission. 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL TU DRESDEN OR HEIDELBERG UNIVERSITY BE LIABLE FOR ANY 25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include "opencv2/opencv.hpp" 41 | 42 | /** Classes and methods for generating random numbers in multi-threaded programs. */ 43 | 44 | /** 45 | * @brief Provides random numbers for multiple threads. 46 | * 47 | * Singelton class. Holds a random number generator for each thread and gives random numbers for the current thread. 48 | */ 49 | class ThreadRand 50 | { 51 | public: 52 | /** 53 | * @brief Returns a random integer (uniform distribution). 54 | * 55 | * @param min Minimum value of the random integer (inclusive). 56 | * @param max Maximum value of the random integer (exclusive). 57 | * @param tid Optional parameter. ID of the thread to use. If not given, the method will obtain the thread ID itself. 58 | * @return int Random integer value. 59 | */ 60 | static int irand(int min, int max, int tid = -1); 61 | 62 | /** 63 | * @brief Returns a random double value (uniform distribution). 64 | * 65 | * @param min Minimum value of the random double (inclusive). 66 | * @param max Maximum value of the random double (inclusive). 67 | * @param tid Optional parameter. ID of the thread to use. If not given, the method will obtain the thread ID itself. 68 | * @return double Random double value. 69 | */ 70 | static double drand(double min, double max, int tid = -1); 71 | 72 | /** 73 | * @brief Returns a random double value (Gauss distribution). 74 | * 75 | * @param mean Mean of the Gauss distribution to sample from. 76 | * @param stdDev Standard deviation of the Gauss distribution to sample from. 77 | * @param tid Optional parameter. ID of the thread to use. If not given, the method will obtain the thread ID itself. 78 | * @return double Random double value. 79 | */ 80 | static double dgauss(double mean, double stdDev, int tid = -1); 81 | 82 | /** 83 | * @brief Re-Initialize the object with the given seed. 84 | * 85 | * @param seed Seed to initialize the random number generators (seed is incremented by one for each generator). 86 | * @return void 87 | */ 88 | static void forceInit(unsigned seed); 89 | 90 | private: 91 | /** 92 | * @brief List of random number generators. One for each thread. 93 | * 94 | */ 95 | static std::vector generators; 96 | /** 97 | * @brief True if the class has been initialized already 98 | */ 99 | static bool initialised; 100 | /** 101 | * @brief Initialize class with the given seed. 102 | * 103 | * Method will create a random number generator for each thread. The given seed 104 | * will be incremented by one for each generator. This methods is automatically 105 | * called when this calss is used the first time. 106 | * 107 | * @param seed Optional parameter. Seed to be used when initializing the generators. Will be incremented by one for each generator. 108 | * @return void 109 | */ 110 | static void init(unsigned seed = 1305); 111 | }; 112 | 113 | /** 114 | * @brief Returns a random integer (uniform distribution). 115 | * 116 | * This method used the ThreadRand class. 117 | * 118 | * @param min Minimum value of the random integer (inclusive). 119 | * @param max Maximum value of the random integer (exclusive). 120 | * @param tid Optional parameter. ID of the thread to use. If not given, the method will obtain the thread ID itself. 121 | * @return int Random integer value. 122 | */ 123 | int irand(int incMin, int excMax, int tid = -1); 124 | /** 125 | * @brief Returns a random double value (uniform distribution). 126 | * 127 | * This method used the ThreadRand class. 128 | * 129 | * @param min Minimum value of the random double (inclusive). 130 | * @param max Maximum value of the random double (inclusive). 131 | * @param tid Optional parameter. ID of the thread to use. If not given, the method will obtain the thread ID itself. 132 | * @return double Random double value. 133 | */ 134 | double drand(double incMin, double incMax, int tid = -1); 135 | 136 | /** 137 | * @brief Returns a random integer value (Gauss distribution). 138 | * 139 | * This method used the ThreadRand class. 140 | * 141 | * @param mean Mean of the Gauss distribution to sample from. 142 | * @param stdDev Standard deviation of the Gauss distribution to sample from. 143 | * @param tid Optional parameter. ID of the thread to use. If not given, the method will obtain the thread ID itself. 144 | * @return double Random integer value. 145 | */ 146 | int igauss(int mean, int stdDev, int tid = -1); 147 | 148 | /** 149 | * @brief Returns a random double value (Gauss distribution). 150 | * 151 | * This method used the ThreadRand class. 152 | * 153 | * @param mean Mean of the Gauss distribution to sample from. 154 | * @param stdDev Standard deviation of the Gauss distribution to sample from. 155 | * @param tid Optional parameter. ID of the thread to use. If not given, the method will obtain the thread ID itself. 156 | * @return double Random double value. 157 | */ 158 | double dgauss(double mean, double stdDev, int tid = -1); 159 | 160 | namespace poseSolver { 161 | 162 | /** 163 | * @brief Inverts a given transformation. 164 | * @param hyp Input transformation. 165 | * @return Inverted transformation. 166 | */ 167 | std::pair getInvHyp(const std::pair& hyp); 168 | 169 | /** 170 | * @brief Maximum of translational error (cm) and rotational error (deg) between two pose hypothesis. 171 | * @param h1 Pose 1. 172 | * @param h2 Pose 2. 173 | * @return Loss. 174 | */ 175 | double maxLoss(const std::pair& h1, const std::pair& h2); 176 | 177 | /** 178 | * @brief Calculates the rotational distance in degree between two transformations. 179 | * Translation will be ignored. 180 | * 181 | * @param h1 Transformation 1. 182 | * @param h2 Transformation 2. 183 | * @return Angle in degree. 184 | */ 185 | double calcAngularDistance(const std::pair& h1, const std::pair& h2); 186 | 187 | /** 188 | * @brief Wrapper around the OpenCV PnP function that returns a zero pose in case PnP fails. See also documentation of cv::solvePnP. 189 | * @param objPts List of 3D points. 190 | * @param imgPts Corresponding 2D points. 191 | * @param camMat Calibration matrix of the camera. 192 | * @param distCoeffs Distortion coefficients. 193 | * @param rot Output parameter. Camera rotation. 194 | * @param trans Output parameter. Camera translation. 195 | * @param extrinsicGuess If true uses input rot and trans as initialization. 196 | * @param methodFlag Specifies the PnP algorithm to be used. 197 | * @return True if PnP succeeds. 198 | */ 199 | inline bool safeSolvePnP( 200 | const std::vector& objPts, 201 | const std::vector& imgPts, 202 | const cv::Mat& camMat, 203 | const cv::Mat& distCoeffs, 204 | cv::Mat& rot, 205 | cv::Mat& trans, 206 | bool extrinsicGuess, 207 | int methodFlag); 208 | 209 | class PnPRANSAC{ 210 | public: 211 | cv::Mat_ camMat; 212 | PnPRANSAC(); 213 | 214 | PnPRANSAC(float fx, float fy, float cx, float cy); 215 | 216 | ~PnPRANSAC(); 217 | 218 | void camMatUpdate(float fx, float fy, float cx, float cy); 219 | 220 | double* RANSACLoop(float* imgPts, float* objPts, int nPts, int objHyps); 221 | }; 222 | 223 | } 224 | 225 | -------------------------------------------------------------------------------- /ufvl_net/models/architecture/pnpransac/pnpransacpy.pxd: -------------------------------------------------------------------------------- 1 | from libcpp cimport bool 2 | from libcpp.vector cimport vector 3 | 4 | cdef extern from "pnpransac.cpp": 5 | pass 6 | 7 | cdef extern from "pnpransac.h" namespace "poseSolver": 8 | cdef cppclass PnPRANSAC: 9 | PnPRANSAC() except + 10 | PnPRANSAC(float, float, float, float) except + 11 | void camMatUpdate(float, float, float, float) 12 | double* RANSACLoop(float*, float*, int, int) -------------------------------------------------------------------------------- /ufvl_net/models/architecture/pnpransac/pnpransacpy.pyx: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | 3 | import numpy as np 4 | cimport numpy as np 5 | 6 | from pnpransacpy cimport PnPRANSAC 7 | 8 | cdef class pnpransac: 9 | cdef PnPRANSAC c_pnpransac 10 | 11 | def __cinit__(self, float fx, float fy, float cx, float cy): 12 | self.c_pnpransac = PnPRANSAC(fx, fy, cx, cy) 13 | 14 | def update_camMat(self, float fx, float fy, float cx, float cy): 15 | self.c_pnpransac.camMatUpdate(fx, fy, cx, cy) 16 | 17 | def RANSAC_loop(self, np.ndarray[double, ndim=2, mode="c"] img_pts, 18 | np.ndarray[double, ndim=2, mode="c"] obj_pts, int n_hyp): 19 | cdef float[:, :] img_pts_ = img_pts.astype(np.float32) 20 | cdef float[:, :] obj_pts_ = obj_pts.astype(np.float32) 21 | cdef int n_pts 22 | n_pts = img_pts_.shape[0] 23 | assert img_pts_.shape[0] == obj_pts_.shape[0] 24 | cdef double* pose 25 | pose = self.c_pnpransac.RANSACLoop(&img_pts_[0,0], &obj_pts_[0,0], 26 | n_pts, n_hyp) 27 | rot = np.array([pose[0],pose[1],pose[2]]) 28 | transl = np.array([pose[3],pose[4],pose[5]]) 29 | return rot, transl -------------------------------------------------------------------------------- /ufvl_net/models/architecture/pnpransac/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | from distutils.core import setup 4 | from distutils.extension import Extension 5 | 6 | from Cython.Distutils import build_ext 7 | from Cython.Build import cythonize 8 | 9 | # where to find opencv headers and libraries 10 | cv_include_dir = os.path.join(sys.prefix, 'include') 11 | cv_library_dir = os.path.join(sys.prefix, 'lib') 12 | 13 | ext_modules = [ 14 | Extension( 15 | "pnpransac", 16 | sources=["pnpransacpy.pyx"], 17 | language="c++", 18 | include_dirs=[cv_include_dir,'/home/dk/.conda/envs/HSCXT/lib/python3.7/site-packages/numpy/core/include/'], 19 | library_dirs=[cv_library_dir], 20 | libraries=['opencv_core','opencv_calib3d'], 21 | extra_compile_args=['-fopenmp','-std=c++11'], 22 | ) 23 | ] 24 | 25 | setup( 26 | name='pnpransac', 27 | cmdclass={'build_ext': build_ext}, 28 | ext_modules=cythonize(ext_modules), 29 | ) -------------------------------------------------------------------------------- /ufvl_net/models/architecture/vis_loc.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import os 3 | import sys 4 | import cv2 5 | import numpy as np 6 | 7 | import torch 8 | import torch.nn as nn 9 | from mmcv.runner import BaseModule 10 | 11 | from ..builder import ARCHITECTURE, build_backbone, build_head 12 | from ..utils.augment import Augments 13 | from .base import BaseClassifier 14 | 15 | @ARCHITECTURE.register_module() 16 | class FDANET(BaseClassifier): 17 | def __init__(self, 18 | backbone, 19 | neck=None, 20 | head=None, 21 | pretrained=None, 22 | train_cfg=None, 23 | init_cfg=None, 24 | dataset="7Scenes"): 25 | super(FDANET, self).__init__(init_cfg) 26 | 27 | self.backbone = build_backbone(backbone) 28 | self.head = build_head(head) 29 | self.loss = EuclideanLoss_with_Uncertainty() 30 | self.dataset = dataset 31 | 32 | def forward_test(self, img, gt_lables=None, **kwargs): 33 | import numpy as np 34 | import sys 35 | import os 36 | import cv2 37 | sys.path.append("/home/dk/ufvl_net/ufvl_net/models/architecture/pnpransac") 38 | import pnpransac 39 | if self.dataset == "7Scenes": 40 | intrinsics_color = np.array([[525.0, 0.0, 320.0], 41 | [0.0, 525.0, 240.0], 42 | [0.0, 0.0, 1.0]]) 43 | elif self.dataset == "12Scenes": 44 | intrinsics_color = np.array([[572.0, 0.0, 320.0], 45 | [0.0, 572.0, 240.0], 46 | [0.0, 0.0, 1.0]]) 47 | pose_solver = pnpransac.pnpransac(intrinsics_color[0, 0], intrinsics_color[1, 1], intrinsics_color[0, 2], 48 | intrinsics_color[1, 2]) 49 | 50 | def get_pose_err(pose_gt, pose_est): 51 | transl_err = np.linalg.norm(pose_gt[0:3, 3] - pose_est[0:3, 3]) 52 | rot_err = pose_est[0:3, 0:3].T.dot(pose_gt[0:3, 0:3]) 53 | rot_err = cv2.Rodrigues(rot_err)[0] # 旋转向量 [3 1] 54 | rot_err = np.reshape(rot_err, (1, 3)) # 旋转向量 [1 3] 55 | rot_err = np.reshape(np.linalg.norm(rot_err, axis=1), -1) / np.pi * 180. # 二范数即转角 56 | return transl_err, rot_err[0] 57 | 58 | x = np.linspace(4, 640 - 4, 80) 59 | y = np.linspace(4, 480 - 4, 60) 60 | 61 | xx, yy = np.meshgrid(x, y) # [60 80] 62 | pcoord = np.concatenate((np.expand_dims(xx, axis=2), np.expand_dims(yy, axis=2)), axis=2) 63 | 64 | x = self.backbone(img) 65 | coord, uncertainty = self.head(x[0]) 66 | coord = np.transpose(coord.cpu().data.numpy()[0, :, :, :], (1, 2, 0)) # [3 60 80]->[60 80 3] 67 | uncertainty = np.transpose(uncertainty[0].cpu().data.numpy(), (1, 2, 0)) 68 | coord = np.concatenate([coord, uncertainty], axis=2) # [60 80 4] 69 | coord = np.ascontiguousarray(coord) 70 | pcoord = np.ascontiguousarray(pcoord) 71 | 72 | pcoord = pcoord.reshape(-1, 2) 73 | coords = coord[:, :, 0:3].reshape(-1, 3) 74 | confidences = coord[:, :, 3].flatten().tolist() 75 | 76 | coords_filtered = [] 77 | coords_filtered_2D = [] 78 | for i in range(len(confidences)): 79 | if confidences[i] > 0: 80 | coords_filtered.append(coords[i]) 81 | coords_filtered_2D.append(pcoord[i]) 82 | 83 | coords_filtered = np.vstack(coords_filtered) 84 | coords_filtered_2D = np.vstack(coords_filtered_2D) 85 | 86 | rot, transl = pose_solver.RANSAC_loop(coords_filtered_2D.astype(np.float64), coords_filtered.astype(np.float64), 87 | 256) # 预测结果,每次取256组点进行PNP Tcw 88 | pose_gt = gt_lables.cpu().numpy()[0, :, :] # [4 4] 89 | pose_est = np.eye(4) # [4 4] 90 | pose_est[0:3, 0:3] = cv2.Rodrigues(rot)[0].T # Rwc 91 | pose_est[0:3, 3] = -np.dot(pose_est[0:3, 0:3], transl) # twc 92 | 93 | transl_err, rot_err = get_pose_err(pose_gt, pose_est) 94 | 95 | return dict(trans_error_med=transl_err, rot_err_med=rot_err) 96 | 97 | def forward_train(self, img, gt_lables, mask, **kwargs): 98 | x = self.backbone(img) 99 | coord, uncer = self.head(x[0]) 100 | loss, accuracy = self.loss(coord, gt_lables, mask, uncer) 101 | losses = dict(loss=loss, accuracy=accuracy) 102 | return losses 103 | 104 | def forward(self, img, gt_lables, mask=None, return_loss=True, **kwargs): 105 | """Calls either forward_train or forward_test depending on whether 106 | return_loss=True. 107 | 108 | Note this setting will change the expected inputs. When 109 | `return_loss=True`, img and img_meta are single-nested (i.e. Tensor and 110 | List[dict]), and when `resturn_loss=False`, img and img_meta should be 111 | double nested (i.e. List[Tensor], List[List[dict]]), with the outer 112 | list indicating test time augmentations. 113 | """ 114 | if return_loss: 115 | assert mask is not None 116 | return self.forward_train(img, gt_lables, mask, **kwargs) 117 | else: 118 | return self.forward_test(img, gt_lables, **kwargs) 119 | 120 | def extract_feat(self, img, stage='neck'): 121 | pass 122 | 123 | def simple_test(self, img, img_metas=None, **kwargs): 124 | pass 125 | 126 | 127 | class EuclideanLoss_with_Uncertainty(nn.Module): 128 | def __init__(self): 129 | super(EuclideanLoss_with_Uncertainty, self).__init__() 130 | self.pdist = nn.PairwiseDistance(p=2) 131 | 132 | def forward(self, pred, target, mask, certainty): 133 | loss_reg = self.pdist(pred.permute(0,2,3,1), target.permute(0,2,3,1)) 134 | certainty_map = torch.clamp(certainty, 1e-6) 135 | loss_map = 3 * torch.log(certainty_map) + loss_reg / (2 * certainty_map.pow(2)) 136 | 137 | loss_map = loss_map * mask 138 | loss =torch.sum(loss_map) / mask.sum() 139 | 140 | if mask is not None: 141 | valid_pixel = mask.sum() + 1 142 | diff_coord_map = mask * loss_reg 143 | 144 | thres_coord_map = torch.clamp(diff_coord_map - 0.05, 0) 145 | num_accurate = valid_pixel - thres_coord_map.nonzero().shape[0] 146 | accuracy = num_accurate / valid_pixel 147 | loss1 = torch.sum(loss_reg*mask) / mask.sum() 148 | return loss, accuracy 149 | -------------------------------------------------------------------------------- /ufvl_net/models/backbones/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .resnet import ResNet, ResNetV1c, ResNetV1d 3 | from .seresnet import SEResNet 4 | 5 | __all__ = [ 6 | 'ResNet', 'ResNetV1c', 'ResNetV1d', 'SEResNet' 7 | ] 8 | -------------------------------------------------------------------------------- /ufvl_net/models/backbones/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/backbones/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/backbones/__pycache__/base_backbone.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/backbones/__pycache__/base_backbone.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/backbones/__pycache__/resnet.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/backbones/__pycache__/resnet.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/backbones/__pycache__/seresnet.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/backbones/__pycache__/seresnet.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/backbones/base_backbone.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from abc import ABCMeta, abstractmethod 3 | 4 | from mmcv.runner import BaseModule 5 | 6 | 7 | class BaseBackbone(BaseModule, metaclass=ABCMeta): 8 | """Base backbone. 9 | 10 | This class defines the basic functions of a backbone. Any backbone that 11 | inherits this class should at least define its own `forward` function. 12 | """ 13 | 14 | def __init__(self, init_cfg=None): 15 | super(BaseBackbone, self).__init__(init_cfg) 16 | 17 | @abstractmethod 18 | def forward(self, x): 19 | """Forward computation. 20 | 21 | Args: 22 | x (tensor | tuple[tensor]): x could be a Torch.tensor or a tuple of 23 | Torch.tensor, containing input data for forward computation. 24 | """ 25 | pass 26 | 27 | def train(self, mode=True): 28 | """Set module status before forward computation. 29 | 30 | Args: 31 | mode (bool): Whether it is train_mode or test_mode 32 | """ 33 | super(BaseBackbone, self).train(mode) 34 | -------------------------------------------------------------------------------- /ufvl_net/models/backbones/seresnet.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import torch.utils.checkpoint as cp 3 | 4 | from ..builder import BACKBONES 5 | from ..utils.se_layer import SELayer 6 | from .resnet import BasicBlock, Bottleneck, ResLayer, ResNet 7 | 8 | 9 | class SEBasicBlock(BasicBlock): 10 | def __init__(self, in_channels, out_channels, se_ratio=16, **kwargs) : 11 | super(SEBasicBlock, self).__init__(in_channels, out_channels, **kwargs) 12 | self.se_layer = SELayer(out_channels, ratio=se_ratio) 13 | 14 | def forward(self, x): 15 | 16 | def _inner_forward(x): 17 | identity = x 18 | 19 | out = self.conv1(x) 20 | out = self.norm1(out) 21 | out = self.relu(out) 22 | 23 | out = self.conv2(out) 24 | out = self.norm2(out) 25 | 26 | out = self.se_layer(out) 27 | if self.downsample is not None: 28 | identity = self.downsample(x) 29 | 30 | out = self.drop_path(out) 31 | 32 | out += identity 33 | 34 | return out 35 | 36 | if self.with_cp and x.requires_grad: 37 | out = cp.checkpoint(_inner_forward, x) 38 | else: 39 | out = _inner_forward(x) 40 | 41 | out = self.relu(out) 42 | 43 | return out 44 | 45 | class SEBottleneck(Bottleneck): 46 | """SEBottleneck block for SEResNet. 47 | 48 | Args: 49 | in_channels (int): The input channels of the SEBottleneck block. 50 | out_channels (int): The output channel of the SEBottleneck block. 51 | se_ratio (int): Squeeze ratio in SELayer. Default: 16 52 | """ 53 | 54 | def __init__(self, in_channels, out_channels, se_ratio=16, **kwargs): 55 | super(SEBottleneck, self).__init__(in_channels, out_channels, **kwargs) 56 | self.se_layer = SELayer(out_channels, ratio=se_ratio) 57 | 58 | def forward(self, x): 59 | 60 | def _inner_forward(x): 61 | identity = x 62 | 63 | out = self.conv1(x) 64 | out = self.norm1(out) 65 | out = self.relu(out) 66 | 67 | out = self.conv2(out) 68 | out = self.norm2(out) 69 | out = self.relu(out) 70 | 71 | out = self.conv3(out) 72 | out = self.norm3(out) 73 | 74 | out = self.se_layer(out) 75 | 76 | if self.downsample is not None: 77 | identity = self.downsample(x) 78 | 79 | out += identity 80 | 81 | return out 82 | 83 | if self.with_cp and x.requires_grad: 84 | out = cp.checkpoint(_inner_forward, x) 85 | else: 86 | out = _inner_forward(x) 87 | 88 | out = self.relu(out) 89 | 90 | return out 91 | 92 | 93 | @BACKBONES.register_module() 94 | class SEResNet(ResNet): 95 | """SEResNet backbone. 96 | 97 | Please refer to the `paper `__ for 98 | details. 99 | 100 | Args: 101 | depth (int): Network depth, from {50, 101, 152}. 102 | se_ratio (int): Squeeze ratio in SELayer. Default: 16. 103 | in_channels (int): Number of input image channels. Default: 3. 104 | stem_channels (int): Output channels of the stem layer. Default: 64. 105 | num_stages (int): Stages of the network. Default: 4. 106 | strides (Sequence[int]): Strides of the first block of each stage. 107 | Default: ``(1, 2, 2, 2)``. 108 | dilations (Sequence[int]): Dilation of each stage. 109 | Default: ``(1, 1, 1, 1)``. 110 | out_indices (Sequence[int]): Output from which stages. If only one 111 | stage is specified, a single tensor (feature map) is returned, 112 | otherwise multiple stages are specified, a tuple of tensors will 113 | be returned. Default: ``(3, )``. 114 | style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two 115 | layer is the 3x3 conv layer, otherwise the stride-two layer is 116 | the first 1x1 conv layer. 117 | deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv. 118 | Default: False. 119 | avg_down (bool): Use AvgPool instead of stride conv when 120 | downsampling in the bottleneck. Default: False. 121 | frozen_stages (int): Stages to be frozen (stop grad and set eval mode). 122 | -1 means not freezing any parameters. Default: -1. 123 | conv_cfg (dict | None): The config dict for conv layers. Default: None. 124 | norm_cfg (dict): The config dict for norm layers. 125 | norm_eval (bool): Whether to set norm layers to eval mode, namely, 126 | freeze running stats (mean and var). Note: Effect on Batch Norm 127 | and its variants only. Default: False. 128 | with_cp (bool): Use checkpoint or not. Using checkpoint will save some 129 | memory while slowing down the training speed. Default: False. 130 | zero_init_residual (bool): Whether to use zero init for last norm layer 131 | in resblocks to let them behave as identity. Default: True. 132 | 133 | Example: 134 | >>> from mmcls.models import SEResNet 135 | >>> import torch 136 | >>> self = SEResNet(depth=50) 137 | >>> self.eval() 138 | >>> inputs = torch.rand(1, 3, 224, 224) 139 | >>> level_outputs = self.forward(inputs) 140 | >>> for level_out in level_outputs: 141 | ... print(tuple(level_out.shape)) 142 | (1, 64, 56, 56) 143 | (1, 128, 28, 28) 144 | (1, 256, 14, 14) 145 | (1, 512, 7, 7) 146 | """ 147 | 148 | arch_settings = { 149 | 18: (SEBasicBlock, (2, 2, 2, 2)), 150 | 34: (SEBasicBlock, (3, 4, 6, 3)), 151 | 50: (SEBottleneck, (3, 4, 6, 3)), 152 | 101: (SEBottleneck, (3, 4, 23, 3)), 153 | 152: (SEBottleneck, (3, 8, 36, 3)) 154 | } 155 | 156 | def __init__(self, depth, se_ratio=16, **kwargs): 157 | if depth not in self.arch_settings: 158 | raise KeyError(f'invalid depth {depth} for SEResNet') 159 | self.se_ratio = se_ratio 160 | super(SEResNet, self).__init__(depth, **kwargs) 161 | 162 | def make_res_layer(self, **kwargs): 163 | return ResLayer(se_ratio=self.se_ratio, **kwargs) 164 | -------------------------------------------------------------------------------- /ufvl_net/models/builder.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmcv.cnn import MODELS as MMCV_MODELS 3 | from mmcv.cnn.bricks.registry import ATTENTION as MMCV_ATTENTION 4 | from mmcv.utils import Registry 5 | 6 | MODELS = Registry('models', parent=MMCV_MODELS) 7 | 8 | BACKBONES = MODELS 9 | HEADS = MODELS 10 | ARCHITECTURE = MODELS 11 | 12 | ATTENTION = Registry('attention', parent=MMCV_ATTENTION) 13 | 14 | 15 | def build_backbone(cfg): 16 | """Build backbone.""" 17 | return BACKBONES.build(cfg) 18 | 19 | 20 | def build_head(cfg): 21 | """Build head.""" 22 | return HEADS.build(cfg) 23 | 24 | 25 | def build_architecture(cfg): 26 | return ARCHITECTURE.build(cfg) 27 | -------------------------------------------------------------------------------- /ufvl_net/models/heads/__init__.py: -------------------------------------------------------------------------------- 1 | from .score_head import RegHead 2 | 3 | __all__ = [ 4 | 'RegHead' 5 | ] 6 | -------------------------------------------------------------------------------- /ufvl_net/models/heads/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/heads/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/heads/__pycache__/score_head.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/heads/__pycache__/score_head.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/heads/score_head.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numbers 3 | 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | from einops import rearrange 8 | from mmcv.cnn import ConvModule 9 | from mmcv.runner import BaseModule 10 | 11 | from ..builder import HEADS 12 | 13 | 14 | @HEADS.register_module() 15 | class RegHead(BaseModule): 16 | def __init__(self, 17 | in_channel=2048, 18 | norm_cfg=dict(type='BN'), 19 | init_cfg=None): 20 | super(RegHead, self).__init__(init_cfg=init_cfg) 21 | 22 | self.in_channel = in_channel 23 | self.conv_reg1 = ConvModule(self.in_channel, 24 | 512, 25 | kernel_size=3, 26 | padding=1, 27 | norm_cfg=norm_cfg) 28 | self.conv_reg2 = ConvModule(512, 29 | 256, 30 | kernel_size=3, 31 | padding=1, 32 | norm_cfg=norm_cfg) 33 | self.conv_reg3 = ConvModule(256, 34 | 128, 35 | kernel_size=3, 36 | padding=1, 37 | norm_cfg=norm_cfg) 38 | self.coord_conv = ConvModule(128, 39 | 64, 40 | kernel_size=3, 41 | norm_cfg=norm_cfg, 42 | padding=1) 43 | 44 | self.coord_reg = torch.nn.Conv2d(64, 3, kernel_size=1) 45 | 46 | self.uncer_conv = ConvModule(128, 47 | 64, 48 | kernel_size=3, 49 | norm_cfg=norm_cfg, 50 | padding=1) 51 | 52 | self.uncer_reg = torch.nn.Conv2d(64, 1, kernel_size=1) 53 | 54 | 55 | def forward(self, feat, **kwargs): 56 | 57 | feat = self.conv_reg3(self.conv_reg2(self.conv_reg1(feat))) 58 | coord = self.coord_reg(self.coord_conv(feat)) 59 | uncer = self.uncer_reg(self.uncer_conv(feat)) 60 | uncer = torch.sigmoid(uncer) 61 | return coord, uncer -------------------------------------------------------------------------------- /ufvl_net/models/utils/__pycache__/make_divisible.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/__pycache__/make_divisible.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/__pycache__/se_layer.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/__pycache__/se_layer.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .augments import Augments 3 | from .cutmix import BatchCutMixLayer 4 | from .identity import Identity 5 | from .mixup import BatchMixupLayer 6 | from .resizemix import BatchResizeMixLayer 7 | 8 | __all__ = ('Augments', 'BatchCutMixLayer', 'Identity', 'BatchMixupLayer', 9 | 'BatchResizeMixLayer') 10 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/augment/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__pycache__/augments.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/augment/__pycache__/augments.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__pycache__/builder.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/augment/__pycache__/builder.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__pycache__/cutmix.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/augment/__pycache__/cutmix.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__pycache__/identity.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/augment/__pycache__/identity.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__pycache__/mixup.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/augment/__pycache__/mixup.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__pycache__/resizemix.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/augment/__pycache__/resizemix.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/__pycache__/utils.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/models/utils/augment/__pycache__/utils.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/augments.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import random 3 | 4 | import numpy as np 5 | 6 | from .builder import build_augment 7 | 8 | 9 | class Augments(object): 10 | """Data augments. 11 | 12 | We implement some data augmentation methods, such as mixup, cutmix. 13 | 14 | Args: 15 | augments_cfg (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict`): 16 | Config dict of augments 17 | 18 | Example: 19 | >>> augments_cfg = [ 20 | dict(type='BatchCutMix', alpha=1., num_classes=10, prob=0.5), 21 | dict(type='BatchMixup', alpha=1., num_classes=10, prob=0.3) 22 | ] 23 | >>> augments = Augments(augments_cfg) 24 | >>> imgs = torch.randn(16, 3, 32, 32) 25 | >>> label = torch.randint(0, 10, (16, )) 26 | >>> imgs, label = augments(imgs, label) 27 | 28 | To decide which augmentation within Augments block is used 29 | the following rule is applied. 30 | We pick augmentation based on the probabilities. In the example above, 31 | we decide if we should use BatchCutMix with probability 0.5, 32 | BatchMixup 0.3. As Identity is not in augments_cfg, we use Identity with 33 | probability 1 - 0.5 - 0.3 = 0.2. 34 | """ 35 | 36 | def __init__(self, augments_cfg): 37 | super(Augments, self).__init__() 38 | 39 | if isinstance(augments_cfg, dict): 40 | augments_cfg = [augments_cfg] 41 | 42 | assert len(augments_cfg) > 0, \ 43 | 'The length of augments_cfg should be positive.' 44 | self.augments = [build_augment(cfg) for cfg in augments_cfg] 45 | self.augment_probs = [aug.prob for aug in self.augments] 46 | 47 | has_identity = any([cfg['type'] == 'Identity' for cfg in augments_cfg]) 48 | if has_identity: 49 | assert sum(self.augment_probs) == 1.0,\ 50 | 'The sum of augmentation probabilities should equal to 1,' \ 51 | ' but got {:.2f}'.format(sum(self.augment_probs)) 52 | else: 53 | assert sum(self.augment_probs) <= 1.0,\ 54 | 'The sum of augmentation probabilities should less than or ' \ 55 | 'equal to 1, but got {:.2f}'.format(sum(self.augment_probs)) 56 | identity_prob = 1 - sum(self.augment_probs) 57 | if identity_prob > 0: 58 | num_classes = self.augments[0].num_classes 59 | self.augments += [ 60 | build_augment( 61 | dict( 62 | type='Identity', 63 | num_classes=num_classes, 64 | prob=identity_prob)) 65 | ] 66 | self.augment_probs += [identity_prob] 67 | 68 | def __call__(self, img, gt_label): 69 | if self.augments: 70 | random_state = np.random.RandomState(random.randint(0, 2**32 - 1)) 71 | aug = random_state.choice(self.augments, p=self.augment_probs) 72 | return aug(img, gt_label) 73 | return img, gt_label 74 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/builder.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmcv.utils import Registry, build_from_cfg 3 | 4 | AUGMENT = Registry('augment') 5 | 6 | 7 | def build_augment(cfg, default_args=None): 8 | return build_from_cfg(cfg, AUGMENT, default_args) 9 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/cutmix.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from abc import ABCMeta, abstractmethod 3 | 4 | import numpy as np 5 | import torch 6 | 7 | from .builder import AUGMENT 8 | from .utils import one_hot_encoding 9 | 10 | 11 | class BaseCutMixLayer(object, metaclass=ABCMeta): 12 | """Base class for CutMixLayer. 13 | 14 | Args: 15 | alpha (float): Parameters for Beta distribution. Positive(>0) 16 | num_classes (int): The number of classes 17 | prob (float): MixUp probability. It should be in range [0, 1]. 18 | Default to 1.0 19 | cutmix_minmax (List[float], optional): cutmix min/max image ratio. 20 | (as percent of image size). When cutmix_minmax is not None, we 21 | generate cutmix bounding-box using cutmix_minmax instead of alpha 22 | correct_lam (bool): Whether to apply lambda correction when cutmix bbox 23 | clipped by image borders. Default to True 24 | """ 25 | 26 | def __init__(self, 27 | alpha, 28 | num_classes, 29 | prob=1.0, 30 | cutmix_minmax=None, 31 | correct_lam=True): 32 | super(BaseCutMixLayer, self).__init__() 33 | 34 | assert isinstance(alpha, float) and alpha > 0 35 | assert isinstance(num_classes, int) 36 | assert isinstance(prob, float) and 0.0 <= prob <= 1.0 37 | 38 | self.alpha = alpha 39 | self.num_classes = num_classes 40 | self.prob = prob 41 | self.cutmix_minmax = cutmix_minmax 42 | self.correct_lam = correct_lam 43 | 44 | def rand_bbox_minmax(self, img_shape, count=None): 45 | """Min-Max CutMix bounding-box Inspired by Darknet cutmix 46 | implementation. It generates a random rectangular bbox based on min/max 47 | percent values applied to each dimension of the input image. 48 | 49 | Typical defaults for minmax are usually in the .2-.3 for min and 50 | .8-.9 range for max. 51 | 52 | Args: 53 | img_shape (tuple): Image shape as tuple 54 | count (int, optional): Number of bbox to generate. Default to None 55 | """ 56 | assert len(self.cutmix_minmax) == 2 57 | img_h, img_w = img_shape[-2:] 58 | cut_h = np.random.randint( 59 | int(img_h * self.cutmix_minmax[0]), 60 | int(img_h * self.cutmix_minmax[1]), 61 | size=count) 62 | cut_w = np.random.randint( 63 | int(img_w * self.cutmix_minmax[0]), 64 | int(img_w * self.cutmix_minmax[1]), 65 | size=count) 66 | yl = np.random.randint(0, img_h - cut_h, size=count) 67 | xl = np.random.randint(0, img_w - cut_w, size=count) 68 | yu = yl + cut_h 69 | xu = xl + cut_w 70 | return yl, yu, xl, xu 71 | 72 | def rand_bbox(self, img_shape, lam, margin=0., count=None): 73 | """Standard CutMix bounding-box that generates a random square bbox 74 | based on lambda value. This implementation includes support for 75 | enforcing a border margin as percent of bbox dimensions. 76 | 77 | Args: 78 | img_shape (tuple): Image shape as tuple 79 | lam (float): Cutmix lambda value 80 | margin (float): Percentage of bbox dimension to enforce as margin 81 | (reduce amount of box outside image). Default to 0. 82 | count (int, optional): Number of bbox to generate. Default to None 83 | """ 84 | ratio = np.sqrt(1 - lam) 85 | img_h, img_w = img_shape[-2:] 86 | cut_h, cut_w = int(img_h * ratio), int(img_w * ratio) 87 | margin_y, margin_x = int(margin * cut_h), int(margin * cut_w) 88 | cy = np.random.randint(0 + margin_y, img_h - margin_y, size=count) 89 | cx = np.random.randint(0 + margin_x, img_w - margin_x, size=count) 90 | yl = np.clip(cy - cut_h // 2, 0, img_h) 91 | yh = np.clip(cy + cut_h // 2, 0, img_h) 92 | xl = np.clip(cx - cut_w // 2, 0, img_w) 93 | xh = np.clip(cx + cut_w // 2, 0, img_w) 94 | return yl, yh, xl, xh 95 | 96 | def cutmix_bbox_and_lam(self, img_shape, lam, count=None): 97 | """Generate bbox and apply lambda correction. 98 | 99 | Args: 100 | img_shape (tuple): Image shape as tuple 101 | lam (float): Cutmix lambda value 102 | count (int, optional): Number of bbox to generate. Default to None 103 | """ 104 | if self.cutmix_minmax is not None: 105 | yl, yu, xl, xu = self.rand_bbox_minmax(img_shape, count=count) 106 | else: 107 | yl, yu, xl, xu = self.rand_bbox(img_shape, lam, count=count) 108 | if self.correct_lam or self.cutmix_minmax is not None: 109 | bbox_area = (yu - yl) * (xu - xl) 110 | lam = 1. - bbox_area / float(img_shape[-2] * img_shape[-1]) 111 | return (yl, yu, xl, xu), lam 112 | 113 | @abstractmethod 114 | def cutmix(self, imgs, gt_label): 115 | pass 116 | 117 | 118 | @AUGMENT.register_module(name='BatchCutMix') 119 | class BatchCutMixLayer(BaseCutMixLayer): 120 | r"""CutMix layer for a batch of data. 121 | 122 | CutMix is a method to improve the network's generalization capability. It's 123 | proposed in `CutMix: Regularization Strategy to Train Strong Classifiers 124 | with Localizable Features ` 125 | 126 | With this method, patches are cut and pasted among training images where 127 | the ground truth labels are also mixed proportionally to the area of the 128 | patches. 129 | 130 | Args: 131 | alpha (float): Parameters for Beta distribution to generate the 132 | mixing ratio. It should be a positive number. More details 133 | can be found in :class:`BatchMixupLayer`. 134 | num_classes (int): The number of classes 135 | prob (float): The probability to execute cutmix. It should be in 136 | range [0, 1]. Defaults to 1.0. 137 | cutmix_minmax (List[float], optional): The min/max area ratio of the 138 | patches. If not None, the bounding-box of patches is uniform 139 | sampled within this ratio range, and the ``alpha`` will be ignored. 140 | Otherwise, the bounding-box is generated according to the 141 | ``alpha``. Defaults to None. 142 | correct_lam (bool): Whether to apply lambda correction when cutmix bbox 143 | clipped by image borders. Defaults to True. 144 | 145 | Note: 146 | If the ``cutmix_minmax`` is None, how to generate the bounding-box of 147 | patches according to the ``alpha``? 148 | 149 | First, generate a :math:`\lambda`, details can be found in 150 | :class:`BatchMixupLayer`. And then, the area ratio of the bounding-box 151 | is calculated by: 152 | 153 | .. math:: 154 | \text{ratio} = \sqrt{1-\lambda} 155 | """ 156 | 157 | def __init__(self, *args, **kwargs): 158 | super(BatchCutMixLayer, self).__init__(*args, **kwargs) 159 | 160 | def cutmix(self, img, gt_label): 161 | one_hot_gt_label = one_hot_encoding(gt_label, self.num_classes) 162 | lam = np.random.beta(self.alpha, self.alpha) 163 | batch_size = img.size(0) 164 | index = torch.randperm(batch_size) 165 | 166 | (bby1, bby2, bbx1, 167 | bbx2), lam = self.cutmix_bbox_and_lam(img.shape, lam) 168 | img[:, :, bby1:bby2, bbx1:bbx2] = \ 169 | img[index, :, bby1:bby2, bbx1:bbx2] 170 | mixed_gt_label = lam * one_hot_gt_label + ( 171 | 1 - lam) * one_hot_gt_label[index, :] 172 | return img, mixed_gt_label 173 | 174 | def __call__(self, img, gt_label): 175 | return self.cutmix(img, gt_label) 176 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/identity.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .builder import AUGMENT 3 | from .utils import one_hot_encoding 4 | 5 | 6 | @AUGMENT.register_module(name='Identity') 7 | class Identity(object): 8 | """Change gt_label to one_hot encoding and keep img as the same. 9 | 10 | Args: 11 | num_classes (int): The number of classes. 12 | prob (float): MixUp probability. It should be in range [0, 1]. 13 | Default to 1.0 14 | """ 15 | 16 | def __init__(self, num_classes, prob=1.0): 17 | super(Identity, self).__init__() 18 | 19 | assert isinstance(num_classes, int) 20 | assert isinstance(prob, float) and 0.0 <= prob <= 1.0 21 | 22 | self.num_classes = num_classes 23 | self.prob = prob 24 | 25 | def one_hot(self, gt_label): 26 | return one_hot_encoding(gt_label, self.num_classes) 27 | 28 | def __call__(self, img, gt_label): 29 | return img, self.one_hot(gt_label) 30 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/mixup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from abc import ABCMeta, abstractmethod 3 | 4 | import numpy as np 5 | import torch 6 | 7 | from .builder import AUGMENT 8 | from .utils import one_hot_encoding 9 | 10 | 11 | class BaseMixupLayer(object, metaclass=ABCMeta): 12 | """Base class for MixupLayer. 13 | 14 | Args: 15 | alpha (float): Parameters for Beta distribution to generate the 16 | mixing ratio. It should be a positive number. 17 | num_classes (int): The number of classes. 18 | prob (float): MixUp probability. It should be in range [0, 1]. 19 | Default to 1.0 20 | """ 21 | 22 | def __init__(self, alpha, num_classes, prob=1.0): 23 | super(BaseMixupLayer, self).__init__() 24 | 25 | assert isinstance(alpha, float) and alpha > 0 26 | assert isinstance(num_classes, int) 27 | assert isinstance(prob, float) and 0.0 <= prob <= 1.0 28 | 29 | self.alpha = alpha 30 | self.num_classes = num_classes 31 | self.prob = prob 32 | 33 | @abstractmethod 34 | def mixup(self, imgs, gt_label): 35 | pass 36 | 37 | 38 | @AUGMENT.register_module(name='BatchMixup') 39 | class BatchMixupLayer(BaseMixupLayer): 40 | r"""Mixup layer for a batch of data. 41 | 42 | Mixup is a method to reduces the memorization of corrupt labels and 43 | increases the robustness to adversarial examples. It's 44 | proposed in `mixup: Beyond Empirical Risk Minimization 45 | ` 46 | 47 | This method simply linearly mix pairs of data and their labels. 48 | 49 | Args: 50 | alpha (float): Parameters for Beta distribution to generate the 51 | mixing ratio. It should be a positive number. More details 52 | are in the note. 53 | num_classes (int): The number of classes. 54 | prob (float): The probability to execute mixup. It should be in 55 | range [0, 1]. Default sto 1.0. 56 | 57 | Note: 58 | The :math:`\alpha` (``alpha``) determines a random distribution 59 | :math:`Beta(\alpha, \alpha)`. For each batch of data, we sample 60 | a mixing ratio (marked as :math:`\lambda`, ``lam``) from the random 61 | distribution. 62 | """ 63 | 64 | def __init__(self, *args, **kwargs): 65 | super(BatchMixupLayer, self).__init__(*args, **kwargs) 66 | 67 | def mixup(self, img, gt_label): 68 | one_hot_gt_label = one_hot_encoding(gt_label, self.num_classes) 69 | lam = np.random.beta(self.alpha, self.alpha) 70 | batch_size = img.size(0) 71 | index = torch.randperm(batch_size) 72 | 73 | mixed_img = lam * img + (1 - lam) * img[index, :] 74 | mixed_gt_label = lam * one_hot_gt_label + ( 75 | 1 - lam) * one_hot_gt_label[index, :] 76 | 77 | return mixed_img, mixed_gt_label 78 | 79 | def __call__(self, img, gt_label): 80 | return self.mixup(img, gt_label) 81 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/resizemix.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import numpy as np 3 | import torch 4 | import torch.nn.functional as F 5 | 6 | from ufvl_net.models.utils.augment.builder import AUGMENT 7 | from .cutmix import BatchCutMixLayer 8 | from .utils import one_hot_encoding 9 | 10 | 11 | @AUGMENT.register_module(name='BatchResizeMix') 12 | class BatchResizeMixLayer(BatchCutMixLayer): 13 | r"""ResizeMix Random Paste layer for a batch of data. 14 | 15 | The ResizeMix will resize an image to a small patch and paste it on another 16 | image. It's proposed in `ResizeMix: Mixing Data with Preserved Object 17 | Information and True Labels `_ 18 | 19 | Args: 20 | alpha (float): Parameters for Beta distribution to generate the 21 | mixing ratio. It should be a positive number. More details 22 | can be found in :class:`BatchMixupLayer`. 23 | num_classes (int): The number of classes. 24 | lam_min(float): The minimum value of lam. Defaults to 0.1. 25 | lam_max(float): The maximum value of lam. Defaults to 0.8. 26 | interpolation (str): algorithm used for upsampling: 27 | 'nearest' | 'linear' | 'bilinear' | 'bicubic' | 'trilinear' | 28 | 'area'. Default to 'bilinear'. 29 | prob (float): The probability to execute resizemix. It should be in 30 | range [0, 1]. Defaults to 1.0. 31 | cutmix_minmax (List[float], optional): The min/max area ratio of the 32 | patches. If not None, the bounding-box of patches is uniform 33 | sampled within this ratio range, and the ``alpha`` will be ignored. 34 | Otherwise, the bounding-box is generated according to the 35 | ``alpha``. Defaults to None. 36 | correct_lam (bool): Whether to apply lambda correction when cutmix bbox 37 | clipped by image borders. Defaults to True 38 | **kwargs: Any other parameters accpeted by :class:`BatchCutMixLayer`. 39 | 40 | Note: 41 | The :math:`\lambda` (``lam``) is the mixing ratio. It's a random 42 | variable which follows :math:`Beta(\alpha, \alpha)` and is mapped 43 | to the range [``lam_min``, ``lam_max``]. 44 | 45 | .. math:: 46 | \lambda = \frac{Beta(\alpha, \alpha)} 47 | {\lambda_{max} - \lambda_{min}} + \lambda_{min} 48 | 49 | And the resize ratio of source images is calculated by :math:`\lambda`: 50 | 51 | .. math:: 52 | \text{ratio} = \sqrt{1-\lambda} 53 | """ 54 | 55 | def __init__(self, 56 | alpha, 57 | num_classes, 58 | lam_min: float = 0.1, 59 | lam_max: float = 0.8, 60 | interpolation='bilinear', 61 | prob=1.0, 62 | cutmix_minmax=None, 63 | correct_lam=True, 64 | **kwargs): 65 | super(BatchResizeMixLayer, self).__init__( 66 | alpha=alpha, 67 | num_classes=num_classes, 68 | prob=prob, 69 | cutmix_minmax=cutmix_minmax, 70 | correct_lam=correct_lam, 71 | **kwargs) 72 | self.lam_min = lam_min 73 | self.lam_max = lam_max 74 | self.interpolation = interpolation 75 | 76 | def cutmix(self, img, gt_label): 77 | one_hot_gt_label = one_hot_encoding(gt_label, self.num_classes) 78 | 79 | lam = np.random.beta(self.alpha, self.alpha) 80 | lam = lam * (self.lam_max - self.lam_min) + self.lam_min 81 | batch_size = img.size(0) 82 | index = torch.randperm(batch_size) 83 | 84 | (bby1, bby2, bbx1, 85 | bbx2), lam = self.cutmix_bbox_and_lam(img.shape, lam) 86 | 87 | img[:, :, bby1:bby2, bbx1:bbx2] = F.interpolate( 88 | img[index], 89 | size=(bby2 - bby1, bbx2 - bbx1), 90 | mode=self.interpolation) 91 | mixed_gt_label = lam * one_hot_gt_label + ( 92 | 1 - lam) * one_hot_gt_label[index, :] 93 | return img, mixed_gt_label 94 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/augment/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import torch.nn.functional as F 3 | 4 | 5 | def one_hot_encoding(gt, num_classes): 6 | """Change gt_label to one_hot encoding. 7 | 8 | If the shape has 2 or more 9 | dimensions, return it without encoding. 10 | Args: 11 | gt (Tensor): The gt label with shape (N,) or shape (N, */). 12 | num_classes (int): The number of classes. 13 | Return: 14 | Tensor: One hot gt label. 15 | """ 16 | if gt.ndim == 1: 17 | # multi-class classification 18 | return F.one_hot(gt, num_classes=num_classes) 19 | else: 20 | # binary classification 21 | # example. [[0], [1], [1]] 22 | # multi-label classification 23 | # example. [[0, 1, 1], [1, 0, 0], [1, 1, 1]] 24 | return gt 25 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/make_divisible.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | def make_divisible(value, divisor, min_value=None, min_ratio=0.9): 3 | """Make divisible function. 4 | 5 | This function rounds the channel number down to the nearest value that can 6 | be divisible by the divisor. 7 | 8 | Args: 9 | value (int): The original channel number. 10 | divisor (int): The divisor to fully divide the channel number. 11 | min_value (int, optional): The minimum value of the output channel. 12 | Default: None, means that the minimum value equal to the divisor. 13 | min_ratio (float): The minimum ratio of the rounded channel 14 | number to the original channel number. Default: 0.9. 15 | Returns: 16 | int: The modified output channel number 17 | """ 18 | 19 | if min_value is None: 20 | min_value = divisor 21 | new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) 22 | # Make sure that round down does not go down by more than (1-min_ratio). 23 | if new_value < min_ratio * value: 24 | new_value += divisor 25 | return new_value 26 | -------------------------------------------------------------------------------- /ufvl_net/models/utils/se_layer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import mmcv 3 | import torch.nn as nn 4 | from mmcv.cnn import ConvModule 5 | from mmcv.runner import BaseModule 6 | 7 | from .make_divisible import make_divisible 8 | 9 | 10 | class SELayer(BaseModule): 11 | """Squeeze-and-Excitation Module. 12 | 13 | Args: 14 | channels (int): The input (and output) channels of the SE layer. 15 | squeeze_channels (None or int): The intermediate channel number of 16 | SElayer. Default: None, means the value of ``squeeze_channels`` 17 | is ``make_divisible(channels // ratio, divisor)``. 18 | ratio (int): Squeeze ratio in SELayer, the intermediate channel will 19 | be ``make_divisible(channels // ratio, divisor)``. Only used when 20 | ``squeeze_channels`` is None. Default: 16. 21 | divisor(int): The divisor to true divide the channel number. Only 22 | used when ``squeeze_channels`` is None. Default: 8. 23 | conv_cfg (None or dict): Config dict for convolution layer. Default: 24 | None, which means using conv2d. 25 | return_weight(bool): Whether to return the weight. Default: False. 26 | act_cfg (dict or Sequence[dict]): Config dict for activation layer. 27 | If act_cfg is a dict, two activation layers will be configurated 28 | by this dict. If act_cfg is a sequence of dicts, the first 29 | activation layer will be configurated by the first dict and the 30 | second activation layer will be configurated by the second dict. 31 | Default: (dict(type='ReLU'), dict(type='Sigmoid')) 32 | """ 33 | 34 | def __init__(self, 35 | channels, 36 | squeeze_channels=None, 37 | ratio=16, 38 | divisor=8, 39 | bias='auto', 40 | conv_cfg=None, 41 | act_cfg=(dict(type='ReLU'), dict(type='Sigmoid')), 42 | return_weight=False, 43 | init_cfg=None): 44 | super(SELayer, self).__init__(init_cfg) 45 | if isinstance(act_cfg, dict): 46 | act_cfg = (act_cfg, act_cfg) 47 | assert len(act_cfg) == 2 48 | assert mmcv.is_tuple_of(act_cfg, dict) 49 | self.global_avgpool = nn.AdaptiveAvgPool2d(1) 50 | if squeeze_channels is None: 51 | squeeze_channels = make_divisible(channels // ratio, divisor) 52 | assert isinstance(squeeze_channels, int) and squeeze_channels > 0, \ 53 | '"squeeze_channels" should be a positive integer, but get ' + \ 54 | f'{squeeze_channels} instead.' 55 | self.return_weight = return_weight 56 | self.conv1 = ConvModule( 57 | in_channels=channels, 58 | out_channels=squeeze_channels, 59 | kernel_size=1, 60 | stride=1, 61 | bias=bias, 62 | conv_cfg=conv_cfg, 63 | act_cfg=act_cfg[0]) 64 | self.conv2 = ConvModule( 65 | in_channels=squeeze_channels, 66 | out_channels=channels, 67 | kernel_size=1, 68 | stride=1, 69 | bias=bias, 70 | conv_cfg=conv_cfg, 71 | act_cfg=act_cfg[1]) 72 | 73 | def forward(self, x): 74 | out = self.global_avgpool(x) 75 | out = self.conv1(out) 76 | out = self.conv2(out) 77 | if self.return_weight: 78 | return out 79 | else: 80 | return x * out 81 | -------------------------------------------------------------------------------- /ufvl_net/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .collect_env import collect_env 3 | from .logger import get_root_logger, load_json_log 4 | from .setup_env import setup_multi_processes 5 | 6 | __all__ = [ 7 | 'collect_env', 'get_root_logger', 'load_json_log', 'setup_multi_processes' 8 | ] 9 | -------------------------------------------------------------------------------- /ufvl_net/utils/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/utils/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/utils/__pycache__/collect_env.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/utils/__pycache__/collect_env.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/utils/__pycache__/logger.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/utils/__pycache__/logger.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/utils/__pycache__/setup_env.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mooncake199809/UFVL-Net/b0976db816c6bf61797603149b9fc96871dd8d9e/ufvl_net/utils/__pycache__/setup_env.cpython-37.pyc -------------------------------------------------------------------------------- /ufvl_net/utils/collect_env.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmcv.utils import collect_env as collect_base_env 3 | from mmcv.utils import get_git_hash 4 | 5 | import ufvl_net 6 | 7 | 8 | def collect_env(): 9 | """Collect the information of the running environments.""" 10 | env_info = collect_base_env() 11 | env_info['MMClassification'] = mmcls.__version__ + '+' + get_git_hash()[:7] 12 | return env_info 13 | 14 | 15 | if __name__ == '__main__': 16 | for name, val in collect_env().items(): 17 | print(f'{name}: {val}') 18 | -------------------------------------------------------------------------------- /ufvl_net/utils/logger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import json 3 | import logging 4 | from collections import defaultdict 5 | 6 | from mmcv.utils import get_logger 7 | 8 | 9 | def get_root_logger(log_file=None, log_level=logging.INFO): 10 | """Get root logger. 11 | 12 | Args: 13 | log_file (str, optional): File path of log. Defaults to None. 14 | log_level (int, optional): The level of logger. 15 | Defaults to :obj:`logging.INFO`. 16 | 17 | Returns: 18 | :obj:`logging.Logger`: The obtained logger 19 | """ 20 | return get_logger('mmcls', log_file, log_level) 21 | 22 | 23 | def load_json_log(json_log): 24 | """load and convert json_logs to log_dicts. 25 | 26 | Args: 27 | json_log (str): The path of the json log file. 28 | 29 | Returns: 30 | dict[int, dict[str, list]]: 31 | Key is the epoch, value is a sub dict. The keys in each sub dict 32 | are different metrics, e.g. memory, bbox_mAP, and the value is a 33 | list of corresponding values in all iterations in this epoch. 34 | 35 | .. code-block:: python 36 | 37 | # An example output 38 | { 39 | 1: {'iter': [100, 200, 300], 'loss': [6.94, 6.73, 6.53]}, 40 | 2: {'iter': [100, 200, 300], 'loss': [6.33, 6.20, 6.07]}, 41 | ... 42 | } 43 | """ 44 | log_dict = dict() 45 | with open(json_log, 'r') as log_file: 46 | for line in log_file: 47 | log = json.loads(line.strip()) 48 | # skip lines without `epoch` field 49 | if 'epoch' not in log: 50 | continue 51 | epoch = log.pop('epoch') 52 | if epoch not in log_dict: 53 | log_dict[epoch] = defaultdict(list) 54 | for k, v in log.items(): 55 | log_dict[epoch][k].append(v) 56 | return log_dict 57 | -------------------------------------------------------------------------------- /ufvl_net/utils/setup_env.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import os 3 | import platform 4 | import warnings 5 | 6 | import cv2 7 | import torch.multiprocessing as mp 8 | 9 | 10 | def setup_multi_processes(cfg): 11 | """Setup multi-processing environment variables.""" 12 | # set multi-process start method as `fork` to speed up the training 13 | if platform.system() != 'Windows': 14 | mp_start_method = cfg.get('mp_start_method', 'fork') 15 | current_method = mp.get_start_method(allow_none=True) 16 | if current_method is not None and current_method != mp_start_method: 17 | warnings.warn( 18 | f'Multi-processing start method `{mp_start_method}` is ' 19 | f'different from the previous setting `{current_method}`.' 20 | f'It will be force set to `{mp_start_method}`. You can change ' 21 | f'this behavior by changing `mp_start_method` in your config.') 22 | mp.set_start_method(mp_start_method, force=True) 23 | 24 | # disable opencv multithreading to avoid system being overloaded 25 | opencv_num_threads = cfg.get('opencv_num_threads', 0) 26 | cv2.setNumThreads(opencv_num_threads) 27 | 28 | # setup OMP threads 29 | # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa 30 | if 'OMP_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: 31 | omp_num_threads = 1 32 | warnings.warn( 33 | f'Setting OMP_NUM_THREADS environment variable for each process ' 34 | f'to be {omp_num_threads} in default, to avoid your system being ' 35 | f'overloaded, please further tune the variable for optimal ' 36 | f'performance in your application as needed.') 37 | os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) 38 | 39 | # setup MKL threads 40 | if 'MKL_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: 41 | mkl_num_threads = 1 42 | warnings.warn( 43 | f'Setting MKL_NUM_THREADS environment variable for each process ' 44 | f'to be {mkl_num_threads} in default, to avoid your system being ' 45 | f'overloaded, please further tune the variable for optimal ' 46 | f'performance in your application as needed.') 47 | os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) 48 | -------------------------------------------------------------------------------- /ufvl_net/version.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved 2 | 3 | __version__ = '0.1.0' 4 | 5 | 6 | def parse_version_info(version_str): 7 | """Parse a version string into a tuple. 8 | 9 | Args: 10 | version_str (str): The version string. 11 | Returns: 12 | tuple[int | str]: The version info, e.g., "1.3.0" is parsed into 13 | (1, 3, 0), and "2.0.0rc1" is parsed into (2, 0, 0, 'rc1'). 14 | """ 15 | version_info = [] 16 | for x in version_str.split('.'): 17 | if x.isdigit(): 18 | version_info.append(int(x)) 19 | elif x.find('rc') != -1: 20 | patch_version = x.split('rc') 21 | version_info.append(int(patch_version[0])) 22 | version_info.append(f'rc{patch_version[1]}') 23 | return tuple(version_info) 24 | 25 | 26 | version_info = parse_version_info(__version__) 27 | 28 | __all__ = ['__version__', 'version_info', 'parse_version_info'] 29 | --------------------------------------------------------------------------------