├── .gitmodules
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── copy_to_results_raw.sh
├── data
├── test.json
└── val.json
├── debug_patches.ipynb
├── detect_sift_keypoints_and_extract_patches.py
├── download_kp2d_weights.sh
├── extract_d2net.py
├── extract_delf.py
├── extract_descriptors_geodesc.py
├── extract_descriptors_hardnet.py
├── extract_descriptors_jit.py
├── extract_descriptors_kornia.py
├── extract_descriptors_l2net.py
├── extract_descriptors_logpolar.py
├── extract_descriptors_sosnet.py
├── extract_kp2d_features.py
├── extract_lanet.py
├── extract_lfnet.py
├── extract_ml_superpoint.py
├── extract_r2d2.py
├── extract_sift_kornia_affnet_desc.py
├── extract_superoint_independent.py
├── generate_image_lists.py
├── generate_yaml.py
├── misc
├── colmap
│ └── extract_colmap_feats_to_db.py
└── l2net
│ ├── convert_l2net_weights_matconv_pytorch.py
│ ├── l2net_model.py
│ └── l2net_ported_weights_lib+.pth
├── run_d2net.sh
├── run_delf.py
├── run_geodesc.sh
├── run_hardnet.sh
├── run_logpolar.sh
├── run_sosnet.sh
├── run_superpoint.sh
├── sp_configs.py
├── system
├── geodesc.yml
├── hardnet.yml
├── lfnet.yml
└── r2d2-python3.6.yml
├── third_party
├── l2net_config
│ ├── convert_l2net_weights_matconv_pytorch.py
│ ├── l2net_model.py
│ ├── l2net_ported_weights_lib+.pth
│ ├── test_batch_img.mat
│ └── test_one.mat
└── superpoint_forked
│ ├── LICENSE
│ └── superpoint.py
└── utils.py
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "third_party/hardnet"]
2 | path = third_party/hardnet
3 | url = https://github.com/DagnyT/hardnet.git
4 | [submodule "third_party/log_polar_descriptors"]
5 | path = third_party/log_polar_descriptors
6 | url = https://github.com/cvlab-epfl/log-polar-descriptors.git
7 | [submodule "third_party/geodesc"]
8 | path = third_party/geodesc
9 | url = https://github.com/lzx551402/geodesc.git
10 | [submodule "third_party/SOSNet"]
11 | path = third_party/SOSNet
12 | url = https://github.com/yuruntian/SOSNet.git
13 | [submodule "third_party/superpoint_ml_repo"]
14 | path = third_party/superpoint_ml_repo
15 | url = https://github.com/MagicLeapResearch/SuperPointPretrainedNetwork
16 | [submodule "third_party/d2net"]
17 | path = third_party/d2net
18 | url = https://github.com/mihaidusmanu/d2-net.git
19 | [submodule "third_party/HardNet2"]
20 | path = third_party/HardNet2
21 | url = https://github.com/pultarmi/HardNet_MultiDataset
22 | [submodule "third_party/r2d2"]
23 | path = third_party/r2d2
24 | url = https://github.com/naver/r2d2.git
25 | [submodule "third_party/contextdesc"]
26 | path = third_party/contextdesc
27 | url = https://github.com/jyh2005xx/contextdesc.git
28 | [submodule "third_party/l2net"]
29 | path = third_party/l2net
30 | url = https://github.com/yuruntian/L2-Net.git
31 | [submodule "third_party/tensorflow_models"]
32 | path = third_party/tensorflow_models
33 | url = https://github.com/tensorflow/models
34 | [submodule "third_party/lfnet"]
35 | path = third_party/lfnet
36 | url = git@github.com:vcg-uvic/lf-net-release.git
37 | [submodule "third_party/KP2D"]
38 | path = third_party/KP2D
39 | url = https://github.com/TRI-ML/KP2D.git
40 | [submodule "third_party/pytorch-superpoint"]
41 | path = third_party/pytorch-superpoint
42 | url = https://github.com/ducha-aiki/pytorch-superpoint.git
43 | [submodule "third_party/lanet"]
44 | path = third_party/lanet
45 | url = https://github.com/wangch-g/lanet.git
46 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows
28 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/).
29 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright Google, University of Victoria (UVIC), Czech Technical
191 | University (CTU, Prague)
192 |
193 | Licensed under the Apache License, Version 2.0 (the "License");
194 | you may not use this file except in compliance with the License.
195 | You may obtain a copy of the License at
196 |
197 | http://www.apache.org/licenses/LICENSE-2.0
198 |
199 | Unless required by applicable law or agreed to in writing, software
200 | distributed under the License is distributed on an "AS IS" BASIS,
201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202 | See the License for the specific language governing permissions and
203 | limitations under the License.
204 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | This repository contains utilities to extract local features for the [Image Matching Benchmark](https://github.com/ubc-vision/image-matching-benchmark) and its associated challenge. For details please refer to the [website](https://image-matching-challenge.github.io).
4 |
5 | ## Data
6 |
7 | Data can be downloaded [here](https://www.cs.ubc.ca/~kmyi/imw2020/data.html): you may want to download the images for validation and testing. Most of the scripts assume that the images are in `../imw-2020`, as follows:
8 |
9 | ```
10 | $ ~/image-matching-benchmark-baselines $ ls ../imw-2020/
11 | british_museum lincoln_memorial_statue milan_cathedral piazza_san_marco sacre_coeur st_pauls_cathedral united_states_capitol
12 | florence_cathedral_side london_bridge mount_rushmore reichstag sagrada_familia st_peters_square
13 |
14 | $ ~/image-matching-benchmark-baselines $ ls ../imw-2020/british_museum/
15 | 00350405_2611802704.jpg 26237164_4796395587.jpg 45839934_4117745134.jpg [...]
16 | ```
17 |
18 | You may need to format the validation set in this way.
19 |
20 | ## Installation
21 |
22 | Initialize the submodules by running the following:
23 | ```
24 | git submodule update --init
25 | ```
26 |
27 | We provide support for the following methods:
28 |
29 | * [Hardnet](https://github.com/DagnyT/hardnet.git)
30 | * [HardnetAmos](https://github.com/pultarmi/HardNet_MultiDataset)
31 | * [GeoDesc](https://github.com/lzx551402/geodesc.git)
32 | * [SOSNet](https://github.com/yuruntian/SOSNet.git)
33 | * [L2Net](https://github.com/yuruntian/L2-Net)
34 | * [Log-polar descriptor](https://github.com/DagnyT/hardnet_ptn.git)
35 | * [Superpoint](https://github.com/MagicLeapResearch/SuperPointPretrainedNetwork)
36 | * [D2-Net](https://github.com/mihaidusmanu/d2-net)
37 | * [DELF](https://github.com/tensorflow/models/blob/master/research/delf/INSTALL_INSTRUCTIONS.md)
38 | * [Contextdesc](https://github.com/lzx551402/contextdesc)
39 | * [LFNet](https://github.com/vcg-uvic/lf-net-release)
40 | * [R2D2](https://github.com/naver/r2d2)
41 |
42 | We have pre-packaged conda environments: see below for details. You can install miniconda following [these instructions](https://docs.conda.io/en/latest/miniconda.html) (we have had problems with the latest version -- consider an [older one](https://repo.continuum.io/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh)). You can install an environment with:
43 | ```
44 | conda env create -f system/.yml
45 | ```
46 |
47 | And switch between them with:
48 | ```
49 | conda deactivate
50 | conda activate
51 | ```
52 |
53 | ## Patch-based descriptors
54 |
55 | ### Pre-extracting patches for patch-based descriptors
56 |
57 | Many learned descriptors require pre-generated patches. This functionality is useful by itself, so we moved it to a [separate package](https://pypi.org/project/extract-patches/). You can install it with `pip install extract_patches`: please note that this requires python 3.6, as the package is generated via nbdev). You may do do this with the `system/r2d2-python3.6.yml` environment (which also requires 3.6 due to formatted string literals) or create a different environment.
58 |
59 | To extract patches with the default configuration to `../benchmark-patches-8k`, run:
60 | ```
61 | python detect_sift_keypoints_and_extract_patches.py
62 | ```
63 |
64 | This will create the following HDF5 files:
65 | ```
66 | $ stat -c "%s %n" ../benchmark-patches-8k/british_museum/*
67 | 6414352 ../benchmark-patches-8k/british_museum/angles.h5
68 | 12789024 ../benchmark-patches-8k/british_museum/keypoints.h5
69 | 2447913728 ../benchmark-patches-8k/british_museum/patches.h5
70 | 6414352 ../benchmark-patches-8k/british_museum/scales.h5
71 | 6414352 ../benchmark-patches-8k/british_museum/scores.h5
72 | ```
73 |
74 | You can also extract patches with a fixed orientation with the flag `--force_upright=no-dups-more-points`: this option will filter out duplicate orientations and add more points until it reaches the keypoint budget (if possible).
75 | ```
76 | python detect_sift_keypoints_and_extract_patches.py --force_upright=no-dups-more-points --folder_outp=../benchmark-patches-8k-upright-no-dups
77 | ```
78 |
79 | These settings generate about (up to) 8000 features per image, which requires lowering the SIFT detection threshold. If you want fewer features (~2k), you may want to use the default detection threshold, as the results are typically slightly better:
80 | ```
81 | python detect_sift_keypoints_and_extract_patches.py --n_keypoints 2048 --folder_outp=../benchmark-patches-default --lower_sift_threshold=False
82 | python detect_sift_keypoints_and_extract_patches.py --n_keypoints 2048 --force_upright=no-dups-more-points --folder_outp=../benchmark-patches-default-upright-no-dups --lower_sift_threshold=False
83 | ```
84 |
85 | After this you can extract features with `run_.sh`, or following the instructions below. The shell scripts use reasonable defaults: please refer to each individual wrapper for further settings (upright patches, different NMS, etc).
86 |
87 | ### Extracting descriptors from pre-generated patches
88 |
89 | For HardNet (environment `hardnet`):
90 | ```
91 | python extract_descriptors_hardnet.py
92 | ```
93 |
94 | For SOSNet (environment `hardnet`):
95 | ```
96 | python extract_descriptors_sosnet.py
97 | ```
98 |
99 | For L2Net (environment `hardnet`):
100 | ```
101 | python extract_descriptors_l2net.py
102 | ```
103 |
104 | The Log-Polar Descriptor (environment `hardnet`) requires access to the original images. For the log-polar models, use:
105 | ```
106 | python extract_descriptors_logpolar.py --config_file=third_party/log_polar_descriptors/configs/init_one_example_ptn_96.yml --method_name=sift8k_8000_logpolar96
107 | ```
108 |
109 | and for the cartesian models, use:
110 | ```
111 | python extract_descriptors_logpolar.py --config_file=third_party/log_polar_descriptors/configs/init_one_example_stn_16.yml --method_name=sift8k_8000_cartesian16
112 | ```
113 |
114 | For Geodesc (environment `geodesc`):
115 | ```
116 | wget http://home.cse.ust.hk/~zluoag/data/geodesc.pb -O third_party/geodesc/model/geodesc.pb
117 | python extract_descriptors_geodesc.py
118 | ```
119 |
120 | Check the files for more options.
121 |
122 | ## End-to-end methods
123 |
124 | ### Superpoint
125 |
126 | Use environment `hardnet`. Keypoints are sorted by score and only the top `num_kp` are kept. You can extract features with default parameters with the following:
127 | ```
128 | python third_party/superpoint_forked/superpoint.py --cuda --num_kp=2048 --method_name=superpoint_default_2048
129 | ```
130 |
131 | You can also lower the detection threshold to extract more features, and resize the images to a fixed size (on the largest dimension), e.g.:
132 | ```
133 | python third_party/superpoint_forked/superpoint.py --cuda --num_kp=8000 --conf_thresh=0.0001 --nms_dist=2 --resize_image_to=1024 --num_kp=8000 --method_name=superpoint_8k_resize1024_nms2
134 | ```
135 |
136 | ### D2-Net
137 |
138 | Use environment `hardnet`. Following D2-Net's settings, you can generate text lists of the images with:
139 | ```
140 | python generate_image_lists.py
141 | ```
142 | Download the weights (use this set, as the default has some overlap with out test subset):
143 | ```bash
144 | mkdir third_party/d2net/models
145 | wget https://dsmn.ml/files/d2-net/d2_tf_no_phototourism.pth -O third_party/d2net/models/d2_tf_no_phototourism.pth
146 | ```
147 | You can then extract single-scale D2-Net features with:
148 | ```
149 | python extract_d2net.py --num_kp=8000 --method_name=d2net-default_8000
150 | ```
151 | and multi-scale D2-Net features (add the `--cpu` flag if your GPU runs out of memory) with:
152 | ```
153 | python extract_d2net.py --num_kp=8000 --multiscale --method_name=d2net-multiscale_8000
154 | ```
155 | (If the multi-scale variant crashes, please check [this](https://github.com/mihaidusmanu/d2-net/issues/22).)
156 |
157 |
158 | ### ContextDesc
159 |
160 | Use environment `hardnet` and download the model weights:
161 | ```
162 | mkdir third_party/contextdesc/pretrained
163 | wget https://research.altizure.com/data/contextdesc_models/contextdesc_pp.tar -O third_party/contextdesc/pretrained/contextdesc_pp.tar
164 | wget https://research.altizure.com/data/contextdesc_models/retrieval_model.tar -O third_party/contextdesc/pretrained/retrieval_model.tar
165 | wget https://research.altizure.com/data/contextdesc_models/contextdesc_pp_upright.tar -O third_party/contextdesc/pretrained/contextdesc_pp_upright.tar
166 | tar -C third_party/contextdesc/pretrained/ -xf third_party/contextdesc/pretrained/contextdesc_pp.tar
167 | tar -C third_party/contextdesc/pretrained/ -xf third_party/contextdesc/pretrained/contextdesc_pp_upright.tar
168 | tar -C third_party/contextdesc/pretrained/ -xf third_party/contextdesc/pretrained/retrieval_model.tar
169 | rm third_party/contextdesc/pretrained/contextdesc_pp.tar
170 | rm third_party/contextdesc/pretrained/contextdesc_pp_upright.tar
171 | rm third_party/contextdesc/pretrained/retrieval_model.tar
172 | ```
173 | Generate the `.yaml` file for ContextDesc:
174 | ```
175 | python generate_yaml.py --num_keypoints=8000
176 | ```
177 | Extract ContextDesc:
178 | ```
179 | python third_party/contextdesc/evaluations.py --config yaml/imw-2020.yaml
180 | ```
181 | You may delete the `tmp` folder after extracting the features:
182 | ```
183 | rm -rf ../benchmark-features/tmp_contextdesc
184 | ```
185 |
186 |
187 | ### DELF
188 |
189 | You can install DELF from the tensorflow models repository, following [these instructions](https://github.com/tensorflow/models/blob/master/research/delf/INSTALL_INSTRUCTIONS.md).
190 |
191 | You have to download the model:
192 | ```
193 | mkdir third_party/tensorflow_models/research/delf/delf/python/examples/parameters/
194 | wget http://storage.googleapis.com/delf/delf_gld_20190411.tar.gz -O third_party/tensorflow_models/research/delf/delf/python/examples/parameters/delf_gld_20190411.tar.gz
195 | tar -C third_party/tensorflow_models/research/delf/delf/python/examples/parameters/ -xvf third_party/tensorflow_models/research/delf/delf/python/examples/parameters/delf_gld_20190411.tar.gz
196 | ```
197 | and add the folder `third_party/tensorflow_models/research` to $PYTHONPATH. See `run_delf.py` for usage.
198 |
199 |
200 | ### LF-Net
201 |
202 | Use environment `lfnet` and download the model weights:
203 | ```
204 | mkdir third_party/lfnet/release
205 | wget https://cs.ubc.ca/research/kmyi_data/files/2018/lf-net/lfnet-norotaug.tar.gz -O third_party/lfnet/release/lfnet-norotaug.tar.gz
206 | tar -C third_party/lfnet/release/ -xf third_party/lfnet/release/lfnet-norotaug.tar.gz
207 | ```
208 | Use environment 'lfnet'. Refer to extract_lfnet.py for more options. Extract LF-Net with default 2K keypoints and without resize image:
209 | ```
210 | python extract_lfnet.py --out_dir=../benchmark-features/lfnet
211 | ```
212 |
213 |
214 | ### R2D2
215 |
216 | Use the environment `r2d2-python-3.6` (requires 3.6 for f-strings). For options, please see the script. The authors provide three pre-trained models which can be used with:
217 |
218 | ```
219 | python extract_r2d2.py --model=third_party/r2d2/models/r2d2_WAF_N16.pt --num_keypoints=8000 --save_path=../benchmark-features/r2d2-waf-n16-8k
220 | python extract_r2d2.py --model=third_party/r2d2/models/r2d2_WASF_N16.pt --num_keypoints=8000 --save_path=../benchmark-features/r2d2-wasf-n16-8k
221 | python extract_r2d2.py --model=third_party/r2d2/models/r2d2_WASF_N8_big.pt --num_keypoints=8000 --save_path=../benchmark-features/r2d2-wasf-n8-big-8k
222 | ```
223 |
224 | ## VLFeat features (via Matlab)
225 |
226 | Matlab-based features are in a [separate repository](https://github.com/ducha-aiki/sfm-benchmark-matlab-features). You can run:
227 | ```bash
228 | ./run_vlfeat_alone.sh
229 | ./run_vlfeat_with_affnet_and_hardnet.sh
230 | ```
231 |
--------------------------------------------------------------------------------
/copy_to_results_raw.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | for fdir in ../benchmark-features/*
4 | do
5 | echo ${fdir}
6 | fname="$(basename "$fdir")"
7 | echo $fname
8 | for seq in ${fdir}/*
9 | do
10 | seqname="$(basename "$seq")";
11 | mkdir ../results_raw_upright/$seqname/$fname
12 | cp $seq/* ../results_raw_upright/$seqname/$fname/
13 | done
14 | done
15 |
--------------------------------------------------------------------------------
/data/test.json:
--------------------------------------------------------------------------------
1 | [
2 | "british_museum",
3 | "florence_cathedral_side",
4 | "lincoln_memorial_statue",
5 | "london_bridge",
6 | "milan_cathedral",
7 | "mount_rushmore",
8 | "piazza_san_marco",
9 | "sagrada_familia",
10 | "st_pauls_cathedral",
11 | "united_states_capitol"
12 | ]
13 |
--------------------------------------------------------------------------------
/data/val.json:
--------------------------------------------------------------------------------
1 | [
2 | "reichstag",
3 | "sacre_coeur",
4 | "st_peters_square"
5 | ]
6 |
--------------------------------------------------------------------------------
/detect_sift_keypoints_and_extract_patches.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import os
3 | import cv2
4 | from tqdm import tqdm
5 | import argparse
6 | import json
7 |
8 | from extract_patches.core import extract_patches
9 | from utils import save_h5
10 |
11 |
12 | def str2bool(v):
13 | if v.lower() in ('yes', 'true', 't', 'y', '1'):
14 | return True
15 | elif v.lower() in ('no', 'false', 'f', 'n', '0'):
16 | return False
17 |
18 |
19 | def l_clahe(img):
20 | clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
21 | lab = cv2.cvtColor(img, cv2.COLOR_RGB2Lab)
22 | lab[:, :, 0] = clahe.apply(lab[:, :, 0])
23 | return cv2.cvtColor(lab, cv2.COLOR_Lab2RGB)
24 |
25 |
26 | def get_SIFT_keypoints(sift, img, lower_detection_th=False):
27 |
28 | # convert to gray-scale and compute SIFT keypoints
29 | keypoints = sift.detect(img, None)
30 |
31 | response = np.array([kp.response for kp in keypoints])
32 | respSort = np.argsort(response)[::-1]
33 |
34 | pt = np.array([kp.pt for kp in keypoints])[respSort]
35 | size = np.array([kp.size for kp in keypoints])[respSort]
36 | angle = np.array([kp.angle for kp in keypoints])[respSort]
37 | response = np.array([kp.response for kp in keypoints])[respSort]
38 |
39 | return pt, size, angle, response
40 |
41 |
42 | if __name__ == '__main__':
43 |
44 | parser = argparse.ArgumentParser()
45 |
46 | parser.add_argument(
47 | "--scenes_folder",
48 | default=os.path.join('..', 'imw-2020'),
49 | help="path to config file",
50 | type=str)
51 | parser.add_argument(
52 | "--folder_outp",
53 | default=os.path.join('..', 'benchmark-patches-8k'),
54 | type=str)
55 | parser.add_argument(
56 | "--mrSize",
57 | default=12.0,
58 | type=float,
59 | help=' patch size in image is mrSize * pt.size. Default mrSize is 12')
60 | parser.add_argument(
61 | "--patchSize",
62 | default=32,
63 | type=int,
64 | help=' patch size in pixels. Default 32')
65 | parser.add_argument(
66 | "--lower_sift_threshold",
67 | default='True',
68 | type=str2bool,
69 | help='Lower detection threshold (useful to extract 8k features)')
70 | parser.add_argument(
71 | "--clahe-mode",
72 | default='None',
73 | type=str,
74 | help='can be None, detector, descriptor, both')
75 | parser.add_argument(
76 | "--subset",
77 | default='both',
78 | type=str,
79 | help='Options: "val", "test", "both", "spc-fix"')
80 | parser.add_argument(
81 | "--force_upright",
82 | default='off',
83 | type=str,
84 | help='Options: "off", "no-dups", "no-dups-more-points"')
85 | parser.add_argument("--n_keypoints", default=8000, type=int)
86 |
87 | args = parser.parse_args()
88 |
89 | if args.subset not in ['val', 'test', 'both', 'spc-fix']:
90 | raise ValueError('Unknown value for --subset')
91 |
92 | if args.lower_sift_threshold:
93 | print('Instantiating SIFT detector with a lower detection threshold')
94 | sift = cv2.xfeatures2d.SIFT_create(
95 | contrastThreshold=-10000, edgeThreshold=-10000)
96 | else:
97 | print('Instantiating SIFT detector with default values')
98 | sift = cv2.xfeatures2d.SIFT_create()
99 |
100 | if not os.path.isdir(args.folder_outp):
101 | os.makedirs(args.folder_outp)
102 |
103 | scenes = []
104 | if args.subset == 'spc-fix':
105 | scenes += ['st_pauls_cathedral']
106 | else:
107 | if args.subset in ['val', 'both']:
108 | with open(os.path.join('data', 'val.json')) as f:
109 | scenes += json.load(f)
110 | if args.subset in ['test', 'both']:
111 | with open(os.path.join('data', 'test.json')) as f:
112 | scenes += json.load(f)
113 | print('Processing the following scenes: {}'.format(scenes))
114 |
115 | suffix = ""
116 | if args.clahe_mode.lower() == 'detector':
117 | suffix = "_clahe_det"
118 | elif args.clahe_mode.lower() == 'descriptor':
119 | suffix = "_clahe_desc"
120 | elif args.clahe_mode.lower() == 'both':
121 | suffix = "_clahe_det_desc"
122 | elif args.clahe_mode.lower() == 'none':
123 | pass
124 | else:
125 | raise ValueError(
126 | "unknown CLAHE mode. Try detector, descriptor or both")
127 |
128 | assert(args.mrSize > 0)
129 | if abs(args.mrSize - 12.) > 0.1:
130 | suffix += '_mrSize{:.1f}'.format(args.mrSize)
131 |
132 | assert(args.patchSize > 0)
133 | if args.patchSize != 32:
134 | suffix += '_patchSize{}'.format(args.patchSize)
135 | for scene in scenes:
136 | print('Processing "{}"'.format(scene))
137 | scene_patches, scene_kp, scene_loc, scene_scale, \
138 | sec_ori, sec_resp = {}, {}, {}, {}, {}, {}
139 |
140 | scene_path = os.path.join(args.scenes_folder,
141 | scene, 'set_100/images/')
142 | num_patches = []
143 | img_list = [x for x in os.listdir(scene_path) if x.endswith('.jpg')]
144 | for im_path in tqdm(img_list):
145 | img_name = im_path.replace('.jpg', '')
146 | im = cv2.cvtColor(
147 | cv2.imread(os.path.join(scene_path, im_path)),
148 | cv2.COLOR_BGR2RGB)
149 | if args.clahe_mode.lower() in ['detector', 'both']:
150 | img_gray = cv2.cvtColor(l_clahe(im), cv2.COLOR_RGB2GRAY)
151 | else:
152 | img_gray = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
153 |
154 | keypoints, scales, angles, responses = get_SIFT_keypoints(sift,
155 | img_gray)
156 |
157 | if args.force_upright == 'off':
158 | # Nothing to do
159 | kpts = [
160 | cv2.KeyPoint(
161 | x=point[0],
162 | y=point[1],
163 | _size=scales[i],
164 | _angle=angles[i]) for i, point in enumerate(keypoints)
165 | ]
166 | elif args.force_upright == 'no-dups':
167 | # Set orientation to zero, remove duplicates later
168 | # This is a subset of the previous set
169 | kpts = [
170 | cv2.KeyPoint(
171 | x=keypoints[i][0],
172 | y=keypoints[i][1],
173 | _size=scales[i],
174 | _angle=0) for i, point in enumerate(keypoints)
175 | ]
176 | elif args.force_upright == 'no-dups-more-points':
177 | # Copy without duplicates, set orientation to zero
178 | # The cropped list may contain new points
179 | kpts = [
180 | cv2.KeyPoint(
181 | x=keypoints[i][0],
182 | y=keypoints[i][1],
183 | _size=scales[i],
184 | _angle=0) for i, point in enumerate(keypoints)
185 | if point not in keypoints[:i]
186 | ]
187 | else:
188 | raise ValueError('Unknown --force_upright setting')
189 |
190 | # apply CLAHE
191 | im = cv2.cvtColor(
192 | cv2.imread(os.path.join(scene_path, im_path)),
193 | cv2.COLOR_BGR2RGB)
194 | if args.clahe_mode.lower() in ['descriptor', 'both']:
195 | im = l_clahe(im)
196 |
197 | # Extract patches
198 | patches = extract_patches(
199 | kpts, im, args.patchSize, args.mrSize)
200 | keypoints = np.array([(x.pt[0], x.pt[1]) for x in kpts ]).reshape(-1, 2)
201 | scales = np.array([args.mrSize * x.size for x in kpts ]).reshape(-1, 1)
202 | angles = np.array([x.angle for x in kpts ]).reshape(-1, 1)
203 | responses = np.array([x.response for x in kpts ]).reshape(-1, 1)
204 |
205 | # Crop
206 | patches = np.array(patches)[:args.n_keypoints].astype(np.uint8)
207 | keypoints = keypoints[:args.n_keypoints]
208 | scales = scales[:args.n_keypoints]
209 | angles = angles[:args.n_keypoints]
210 | responses = responses[:args.n_keypoints]
211 |
212 | # Remove duplicates after cropping
213 | if args.force_upright == 'no-dups':
214 | _, unique = np.unique(keypoints, axis=0, return_index=True)
215 | patches = patches[unique]
216 | keypoints = keypoints[unique]
217 | scales = scales[unique]
218 | angles = angles[unique]
219 | responses = responses[unique]
220 |
221 | # Patches are already uint8
222 | num_patches.append(patches.shape[0])
223 |
224 | scene_patches[img_name] = patches
225 | scene_kp[img_name] = keypoints
226 | scene_scale[img_name] = scales
227 | sec_ori[img_name] = angles
228 | sec_resp[img_name] = responses
229 |
230 | print('Processed {} images: {} patches/image'.format(
231 | len(num_patches), np.array(num_patches).mean()))
232 |
233 | cur_path = os.path.join(args.folder_outp, scene)
234 | # if args.force_upright == 'no-dups':
235 | # cur_path += '_upright_v1'
236 | # elif args.force_upright == 'no-dups-more-points':
237 | # cur_path += '_upright_v2'
238 | if not os.path.isdir(cur_path):
239 | os.makedirs(cur_path)
240 |
241 | save_h5(scene_patches,
242 | os.path.join(cur_path, 'patches{}.h5'.format(suffix)))
243 | save_h5(scene_kp, os.path.join(cur_path,
244 | 'keypoints{}.h5'.format(suffix)))
245 | save_h5(scene_scale, os.path.join(cur_path,
246 | 'scales{}.h5'.format(suffix)))
247 | save_h5(sec_ori, os.path.join(cur_path, 'angles{}.h5'.format(suffix)))
248 | save_h5(sec_resp, os.path.join(cur_path, 'scores{}.h5'.format(suffix)))
249 |
250 | print('Done!')
251 |
--------------------------------------------------------------------------------
/download_kp2d_weights.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | mkdir third_party/KP2D/data
3 | mkdir third_party/KP2D/data/models
4 | mkdir third_party/KP2D/data/models/kp2d/
5 | wget https://tri-ml-public.s3.amazonaws.com/github/kp2d/models/pretrained_models.tar.gz
6 | mv pretrained_models.tar.gz third_party/KP2D/data/models/kp2d/
7 | cd third_party/KP2D/data/models/kp2d/ && tar -xzf pretrained_models.tar.gz
8 |
--------------------------------------------------------------------------------
/extract_d2net.py:
--------------------------------------------------------------------------------
1 | # Forked from https://github.com/mihaidusmanu/d2-net:extract_features.py
2 |
3 | import argparse
4 | import numpy as np
5 | import imageio
6 | import torch
7 | import json
8 | from tqdm import tqdm
9 | from time import time
10 | import os
11 | import sys
12 |
13 | import scipy
14 | import scipy.io
15 | import scipy.misc
16 |
17 | sys.path.append(os.path.join('third_party', 'd2net'))
18 | from lib.model_test import D2Net
19 | from lib.utils import preprocess_image
20 | from lib.pyramid import process_multiscale
21 | from utils import save_h5
22 |
23 |
24 | # Argument parsing
25 | parser = argparse.ArgumentParser(description='Feature extraction script')
26 |
27 | parser.add_argument(
28 | "--save_path",
29 | default=os.path.join('..', 'benchmark-features'),
30 | type=str,
31 | help='Path to store the features')
32 |
33 | parser.add_argument(
34 | "--method_name", default='d2-net-singlescale_8000', type=str)
35 |
36 | parser.add_argument(
37 | '--preprocessing', type=str, default='caffe',
38 | help='image preprocessing (caffe or torch)'
39 | )
40 | parser.add_argument(
41 | '--model_file', type=str,
42 | default=os.path.join(
43 | 'third_party', 'd2net', 'models', 'd2_tf_no_phototourism.pth'),
44 | help='path to the full model'
45 | )
46 |
47 | parser.add_argument(
48 | '--max_edge', type=int, default=1600,
49 | help='maximum image size at network input'
50 | )
51 | parser.add_argument(
52 | '--max_sum_edges', type=int, default=2800,
53 | help='maximum sum of image sizes at network input'
54 | )
55 |
56 | parser.add_argument(
57 | '--multiscale', dest='multiscale', action='store_true',
58 | help='extract multiscale features'
59 | )
60 | parser.set_defaults(multiscale=False)
61 |
62 | parser.add_argument(
63 | '--no-relu', dest='use_relu', action='store_false',
64 | help='remove ReLU after the dense feature extraction module'
65 | )
66 | parser.set_defaults(use_relu=True)
67 |
68 | parser.add_argument(
69 | '--cpu', dest='cpu', action='store_true',
70 | help='Use CPU instead of GPU'
71 | )
72 |
73 | parser.add_argument(
74 | '--num_kp',
75 | type=int,
76 | default=0,
77 | help='Number of keypoints to save (0 to keep all)')
78 |
79 | parser.set_defaults(cpu=False)
80 |
81 | parser.add_argument(
82 | "--subset",
83 | default='both',
84 | type=str,
85 | help='Options: "val", "test", "both", "spc-fix"')
86 |
87 | args, unparsed = parser.parse_known_args()
88 |
89 | # Parse dataset
90 | if args.subset not in ['val', 'test', 'both', 'spc-fix']:
91 | raise ValueError('Unknown value for --subset')
92 | seqs = []
93 | if args.subset == 'spc-fix':
94 | seqs += ['st_pauls_cathedral']
95 | else:
96 | if args.subset in ['val', 'both']:
97 | with open(os.path.join('data', 'val.json')) as f:
98 | seqs += json.load(f)
99 | if args.subset in ['test', 'both']:
100 | with open(os.path.join('data', 'test.json')) as f:
101 | seqs += json.load(f)
102 | print('Processing the following scenes: {}'.format(seqs))
103 |
104 | # CUDA
105 | if args.cpu:
106 | print('Using CPU')
107 | use_cuda = False
108 | device = None
109 | else:
110 | print('Using GPU (if available)')
111 | use_cuda = torch.cuda.is_available()
112 | device = torch.device("cuda:0" if use_cuda else "cpu")
113 |
114 | print(args)
115 |
116 | # Creating CNN model
117 | model = D2Net(
118 | model_file=args.model_file,
119 | use_relu=args.use_relu,
120 | use_cuda=use_cuda
121 | )
122 |
123 | for seq in seqs:
124 | print('Processing "{}"'.format(seq))
125 | seq_keypoints = {}
126 | seq_scores = {}
127 | seq_descriptors = {}
128 | seq_scales = {}
129 |
130 | # Open the text file
131 | with open(os.path.join('txt', 'list-{}.txt'.format(seq)), 'r') as f:
132 | lines = f.readlines()
133 |
134 | # Process each image
135 | for line in tqdm(lines, total=len(lines)):
136 | path = line.strip()
137 | key = os.path.basename(os.path.splitext(path)[0])
138 |
139 | image = imageio.imread(path)
140 | if len(image.shape) == 2:
141 | image = image[:, :, np.newaxis]
142 | image = np.repeat(image, 3, -1)
143 |
144 | # TODO: switch to PIL.Image due to deprecation of scipy.misc.imresize.
145 | resized_image = image
146 | if max(resized_image.shape) > args.max_edge:
147 | resized_image = scipy.misc.imresize(
148 | resized_image,
149 | args.max_edge / max(resized_image.shape)
150 | ).astype('float')
151 | if sum(resized_image.shape[: 2]) > args.max_sum_edges:
152 | resized_image = scipy.misc.imresize(
153 | resized_image,
154 | args.max_sum_edges / sum(resized_image.shape[: 2])
155 | ).astype('float')
156 |
157 | fact_i = image.shape[0] / resized_image.shape[0]
158 | fact_j = image.shape[1] / resized_image.shape[1]
159 |
160 | t_start = time()
161 | input_image = preprocess_image(
162 | resized_image,
163 | preprocessing=args.preprocessing
164 | )
165 | with torch.no_grad():
166 | if args.multiscale:
167 | keypoints, scores, descriptors = process_multiscale(
168 | torch.tensor(
169 | input_image[np.newaxis, :, :, :].astype(np.float32),
170 | device=device
171 | ),
172 | model
173 | )
174 | else:
175 | keypoints, scores, descriptors = process_multiscale(
176 | torch.tensor(
177 | input_image[np.newaxis, :, :, :].astype(np.float32),
178 | device=device
179 | ),
180 | model,
181 | scales=[1]
182 | )
183 | t_end = time()
184 |
185 | # Input image coordinates
186 | keypoints[:, 0] *= fact_i
187 | keypoints[:, 1] *= fact_j
188 |
189 | # Sort the scores and subsample
190 | indices = np.argsort(scores)[::-1]
191 | if args.num_kp > 0:
192 | top_k = indices[:args.num_kp]
193 | else:
194 | top_k = indices
195 |
196 | # Flip coordinates: network provides [y, x]
197 | seq_keypoints[key] = np.concatenate(
198 | [keypoints[top_k, 1][..., None],
199 | keypoints[top_k, 0][..., None]],
200 | axis=1)
201 | seq_scales[key] = keypoints[top_k, 2]
202 | seq_scores[key] = scores[top_k]
203 | seq_descriptors[key] = descriptors[top_k, :]
204 |
205 | # print('Processed "{}" in {:.02f} sec. Found {} features'.format(
206 | # key, t_end - t_start, keypoints.shape[0]))
207 |
208 | print('Average number of keypoints per image: {:.02f}'.format(
209 | np.mean([v.shape[0] for v in seq_keypoints.values()])))
210 |
211 | cur_path = os.path.join(args.save_path, args.method_name, seq)
212 | if not os.path.exists(cur_path):
213 | os.makedirs(cur_path)
214 | save_h5(seq_descriptors, os.path.join(cur_path, 'descriptors.h5'))
215 | save_h5(seq_keypoints, os.path.join(cur_path, 'keypoints.h5'))
216 | save_h5(seq_scores, os.path.join(cur_path, 'scores.h5'))
217 | save_h5(seq_scales, os.path.join(cur_path, 'scales.h5'))
218 |
219 | print('Done')
220 |
--------------------------------------------------------------------------------
/extract_delf.py:
--------------------------------------------------------------------------------
1 | # Copyright 2017 The TensorFlow Authors All Rights Reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 | # Forked from:
16 | # https://github.com/tensorflow/models/blob/master/research/delf/delf/python/examples/extract_features.py
17 | """Extracts DELF features from a list of images, saving them to file.
18 |
19 | The images must be in JPG format. The program checks if descriptors already
20 | exist, and skips computation for those.
21 | """
22 |
23 | from __future__ import absolute_import
24 | from __future__ import division
25 | from __future__ import print_function
26 |
27 | import argparse
28 | import os
29 | import sys
30 | import time
31 | import json
32 | import numpy as np
33 | import h5py
34 | import tensorflow as tf
35 |
36 | from google.protobuf import text_format
37 | from tensorflow.python.platform import app
38 | from delf import delf_config_pb2
39 | from delf import feature_extractor
40 | from delf import feature_io
41 |
42 | cmd_args = None
43 |
44 | # Extension of feature files.
45 | _DELF_EXT = '.h5'
46 |
47 | # Pace to report extraction log.
48 | _STATUS_CHECK_ITERATIONS = 100
49 |
50 |
51 | def _ReadImageList(list_path):
52 | """Helper function to read image paths.
53 |
54 | Args:
55 | list_path: Path to list of images, one image path per line.
56 |
57 | Returns:
58 | image_paths: List of image paths.
59 | """
60 | with tf.gfile.GFile(list_path, 'r') as f:
61 | image_paths = f.readlines()
62 | image_paths = [entry.rstrip() for entry in image_paths]
63 | return image_paths
64 |
65 |
66 | def MakeExtractor(sess, config, import_scope=None):
67 | """Creates a function to extract features from an image.
68 |
69 | Args:
70 | sess: TensorFlow session to use.
71 | config: DelfConfig proto containing the model configuration.
72 | import_scope: Optional scope to use for model.
73 |
74 | Returns:
75 | Function that receives an image and returns features.
76 | """
77 | tf.saved_model.loader.load(
78 | sess, [tf.saved_model.tag_constants.SERVING],
79 | config.model_path,
80 | import_scope=import_scope)
81 | import_scope_prefix = import_scope + '/' if import_scope is not None else ''
82 | input_image = sess.graph.get_tensor_by_name('%sinput_image:0' %
83 | import_scope_prefix)
84 | input_score_threshold = sess.graph.get_tensor_by_name(
85 | '%sinput_abs_thres:0' % import_scope_prefix)
86 | input_image_scales = sess.graph.get_tensor_by_name('%sinput_scales:0' %
87 | import_scope_prefix)
88 | input_max_feature_num = sess.graph.get_tensor_by_name(
89 | '%sinput_max_feature_num:0' % import_scope_prefix)
90 | boxes = sess.graph.get_tensor_by_name('%sboxes:0' % import_scope_prefix)
91 | raw_descriptors = sess.graph.get_tensor_by_name('%sfeatures:0' %
92 | import_scope_prefix)
93 | feature_scales = sess.graph.get_tensor_by_name('%sscales:0' %
94 | import_scope_prefix)
95 | attention_with_extra_dim = sess.graph.get_tensor_by_name(
96 | '%sscores:0' % import_scope_prefix)
97 | attention = tf.reshape(attention_with_extra_dim,
98 | [tf.shape(attention_with_extra_dim)[0]])
99 |
100 | locations, descriptors = feature_extractor.DelfFeaturePostProcessing(
101 | boxes, raw_descriptors, config)
102 |
103 | def ExtractorFn(image):
104 | """Receives an image and returns DELF features.
105 |
106 | Args:
107 | image: Uint8 array with shape (height, width 3) containing the RGB image.
108 |
109 | Returns:
110 | Tuple (locations, descriptors, feature_scales, attention)
111 | """
112 | return sess.run([locations, descriptors, feature_scales, attention],
113 | feed_dict={
114 | input_image: image,
115 | input_score_threshold:
116 | config.delf_local_config.score_threshold,
117 | input_image_scales: list(config.image_scales),
118 | input_max_feature_num:
119 | config.delf_local_config.max_feature_num
120 | })
121 |
122 | return ExtractorFn
123 |
124 |
125 | def main(unused_argv):
126 | tf.logging.set_verbosity(tf.logging.INFO)
127 |
128 | # Read list of images.
129 | tf.logging.info('Reading list of images...')
130 | image_paths = _ReadImageList(cmd_args.list_images_path)
131 | num_images = len(image_paths)
132 | tf.logging.info('done! Found %d images', num_images)
133 |
134 | # Parse DelfConfig proto.
135 | config = delf_config_pb2.DelfConfig()
136 | with tf.gfile.FastGFile(cmd_args.config_path, 'r') as f:
137 | text_format.Merge(f.read(), config)
138 |
139 | # Create output directory if necessary.
140 | if not os.path.exists(cmd_args.output_dir):
141 | os.makedirs(cmd_args.output_dir)
142 |
143 | # Tell TensorFlow that the model will be built into the default Graph.
144 | with tf.Graph().as_default():
145 | # Reading list of images.
146 | filename_queue = tf.train.string_input_producer(
147 | image_paths, shuffle=False)
148 | reader = tf.WholeFileReader()
149 | _, value = reader.read(filename_queue)
150 | image_tf = tf.image.decode_jpeg(value, channels=3)
151 |
152 | with tf.Session() as sess:
153 | init_op = tf.global_variables_initializer()
154 | sess.run(init_op)
155 |
156 | extractor_fn = MakeExtractor(sess, config)
157 |
158 | # Start input enqueue threads.
159 | coord = tf.train.Coordinator()
160 | threads = tf.train.start_queue_runners(sess=sess, coord=coord)
161 | start = time.clock()
162 |
163 | with h5py.File(os.path.join(cmd_args.output_dir, 'keypoints.h5'), 'w') as h5_kp, \
164 | h5py.File(os.path.join(cmd_args.output_dir, 'descriptors.h5'), 'w') as h5_desc, \
165 | h5py.File(os.path.join(cmd_args.output_dir, 'scores.h5'), 'w') as h5_score, \
166 | h5py.File(os.path.join(cmd_args.output_dir, 'scales.h5'), 'w') as h5_scale:
167 | for i in range(num_images):
168 | key = os.path.splitext(os.path.basename(image_paths[i]))[0]
169 | print('Processing "{}"'.format(key))
170 |
171 | # Write to log-info once in a while.
172 | if i == 0:
173 | tf.logging.info(
174 | 'Starting to extract DELF features from images...')
175 | elif i % _STATUS_CHECK_ITERATIONS == 0:
176 | elapsed = (time.clock() - start)
177 | tf.logging.info(
178 | 'Processing image %d out of %d, last %d '
179 | 'images took %f seconds', i, num_images,
180 | _STATUS_CHECK_ITERATIONS, elapsed)
181 | start = time.clock()
182 |
183 | # # Get next image.
184 | im = sess.run(image_tf)
185 |
186 | # If descriptor already exists, skip its computation.
187 | # out_desc_filename = os.path.splitext(os.path.basename(
188 | # image_paths[i]))[0] + _DELF_EXT
189 | # out_desc_fullpath = os.path.join(cmd_args.output_dir, out_desc_filename)
190 | # if tf.gfile.Exists(out_desc_fullpath):
191 | # tf.logging.info('Skipping %s', image_paths[i])
192 | # continue
193 |
194 | # Extract and save features.
195 | (locations_out, descriptors_out, feature_scales_out,
196 | attention_out) = extractor_fn(im)
197 |
198 | # np.savez('{}.npz'.format(config.delf_local_config.max_feature_num), keypoints=locations_out)
199 |
200 | # feature_io.WriteToFile(out_desc_fullpath, locations_out,
201 | # feature_scales_out, descriptors_out,
202 | # attention_out)
203 | h5_kp[key] = locations_out[:, ::-1]
204 | h5_desc[key] = descriptors_out
205 | h5_scale[key] = feature_scales_out
206 | h5_score[key] = attention_out
207 |
208 | # Finalize enqueue threads.
209 | coord.request_stop()
210 | coord.join(threads)
211 |
212 |
213 | if __name__ == '__main__':
214 | parser = argparse.ArgumentParser()
215 | parser.register('type', 'bool', lambda v: v.lower() == 'true')
216 | parser.add_argument(
217 | '--config_path',
218 | type=str,
219 | default='misc/delf/delf_config_example.pbtxt',
220 | help="""
221 | Path to DelfConfig proto text file with configuration to be used for DELF
222 | extraction.
223 | """)
224 | parser.add_argument(
225 | '--list_images_path',
226 | type=str,
227 | help="""
228 | Path to list of images whose DELF features will be extracted.
229 | """)
230 | parser.add_argument(
231 | '--output_dir',
232 | type=str,
233 | default='../benchmark-features/delf',
234 | help="""
235 | Directory where DELF features will be written to. Each image's features
236 | will be written to a file with same name, and extension replaced by .delf.
237 | """)
238 |
239 | cmd_args, unparsed = parser.parse_known_args()
240 | app.run(main=main, argv=[sys.argv[0]] + unparsed)
241 |
--------------------------------------------------------------------------------
/extract_descriptors_geodesc.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import numpy as np
3 | import argparse
4 | import h5py
5 | from tqdm import tqdm
6 | import os
7 | import sys
8 | import shutil
9 | import json
10 |
11 | from utils import cv2_greyscale, cv2_scale, np_reshape, str2bool, save_h5
12 | import tensorflow as tf
13 | import torchvision.transforms as transforms
14 |
15 | sys.path.append(os.path.join('third_party', 'geodesc'))
16 | from third_party.geodesc.utils.tf import load_frozen_model
17 |
18 |
19 | def get_transforms():
20 |
21 | transform = transforms.Compose([
22 | transforms.Lambda(cv2_greyscale), transforms.Lambda(cv2_scale),
23 | transforms.Lambda(np_reshape)
24 | ])
25 |
26 | return transform
27 |
28 |
29 | if __name__ == '__main__':
30 | parser = argparse.ArgumentParser()
31 | parser.add_argument(
32 | "--dataset_path",
33 | default=os.path.join('..', 'benchmark-patches-8k'),
34 | type=str,
35 | help='Path to the pre-generated patches')
36 | parser.add_argument(
37 | "--save_path",
38 | default=os.path.join('..', 'benchmark-features'),
39 | type=str,
40 | help='Path to store the features')
41 | parser.add_argument(
42 | "--method_name", default='sift8k_8000_geodesc', type=str)
43 | parser.add_argument(
44 | "--weights_path",
45 | default=os.path.join('third_party', 'geodesc', 'model', 'geodesc.pb'),
46 | type=str,
47 | help='Path to the model weights')
48 | parser.add_argument(
49 | "--subset",
50 | default='both',
51 | type=str,
52 | help='Options: "val", "test", "both", "spc-fix", "lms-fix"')
53 | parser.add_argument(
54 | "--clahe-mode",
55 | default='None',
56 | type=str,
57 | help='can be None, detector, descriptor, both')
58 |
59 | args = parser.parse_args()
60 |
61 | if args.subset not in ['val', 'test', 'both', 'spc-fix', 'lms-fix']:
62 | raise ValueError('Unknown value for --subset')
63 | seqs = []
64 | if args.subset == 'spc-fix':
65 | seqs += ['st_pauls_cathedral']
66 | elif args.subset == 'lms-fix':
67 | seqs += ['lincoln_memorial_statue']
68 | else:
69 | if args.subset in ['val', 'both']:
70 | with open(os.path.join('data', 'val.json')) as f:
71 | seqs += json.load(f)
72 | if args.subset in ['test', 'both']:
73 | with open(os.path.join('data', 'test.json')) as f:
74 | seqs += json.load(f)
75 | print('Processing the following scenes: {}'.format(seqs))
76 |
77 | suffix = ""
78 | if args.clahe_mode.lower() == 'detector':
79 | suffix = "_clahe_det"
80 | elif args.clahe_mode.lower() == 'descriptor':
81 | suffix = "_clahe_desc"
82 | elif args.clahe_mode.lower() == 'both':
83 | suffix = "_clahe_det_desc"
84 | elif args.clahe_mode.lower() == 'none':
85 | pass
86 | else:
87 | raise ValueError("unknown CLAHE mode. Try detector, descriptor or both")
88 | args.method_name += suffix
89 |
90 | print('Saving descriptors to folder: {}'.format(args.method_name))
91 |
92 | transforms = get_transforms()
93 |
94 | graph = load_frozen_model(args.weights_path, print_nodes=False)
95 |
96 | with tf.Session(graph=graph) as sess:
97 | for idx, seq_name in enumerate(seqs):
98 | print('Processing "{}"'.format(seq_name))
99 |
100 | seq_descriptors = {}
101 | patches_h5py_file = os.path.join(args.dataset_path, seq_name,
102 | 'patches{}.h5'.format(suffix))
103 |
104 | with h5py.File(patches_h5py_file, 'r') as patches_h5py:
105 | for key, patches in tqdm(patches_h5py.items()):
106 | patches = patches.value
107 | bs = 128
108 | descriptors = []
109 |
110 | for i in range(0, len(patches), bs):
111 | seq_data = patches[i:i + bs, :, :, :]
112 | seq_data = np.array(
113 | [transforms(patch)
114 | for patch in seq_data]).squeeze(axis=3)
115 | # compute output
116 | processed_seq = np.zeros(
117 | (len(seq_data), 32, 32), np.float32)
118 |
119 | for j in range(len(seq_data)):
120 | processed_seq[j] = (seq_data[j] - np.mean(
121 | seq_data[j])) / (np.std(seq_data[j]) + 1e-8)
122 |
123 | processed_seq = np.expand_dims(processed_seq, axis=-1)
124 |
125 | descs = sess.run("squeeze_1:0",
126 | feed_dict={"input:0": processed_seq})
127 | if descs.ndim == 1:
128 | descs = descs[None, ...]
129 | descriptors.extend(descs)
130 |
131 | descriptors = np.array(descriptors)
132 | seq_descriptors[key] = descriptors.astype(np.float32)
133 |
134 | print('Processed {} images: {} descriptors/image'.format(
135 | len(seq_descriptors),
136 | np.array([s.shape[0]
137 | for s in seq_descriptors.values()]).mean()))
138 |
139 | cur_path = os.path.join(args.save_path, args.method_name, seq_name)
140 | if not os.path.exists(cur_path):
141 | os.makedirs(cur_path)
142 | save_h5(seq_descriptors, os.path.join(cur_path, 'descriptors.h5'))
143 | sub_files_in = ['keypoints{}.h5'.format(suffix), 'scales{}.h5'.format(suffix), 'angles{}.h5'.format(suffix), 'scores{}.h5'.format(suffix)]
144 | sub_files_out = ['keypoints.h5', 'scales.h5', 'angles.h5', 'scores.h5']
145 | for sub_file_in, sub_file_out in zip(sub_files_in, sub_files_out):
146 | shutil.copyfile(
147 | os.path.join(args.dataset_path, seq_name, sub_file_in),
148 | os.path.join(cur_path, sub_file_out))
149 |
150 | print('Done sequence: {}'.format(seq_name))
151 |
--------------------------------------------------------------------------------
/extract_descriptors_hardnet.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import numpy as np
3 | import argparse
4 | import h5py
5 | from tqdm import tqdm
6 | import os
7 | import sys
8 | import shutil
9 | import json
10 |
11 | import torchvision.transforms as transforms
12 | from utils import cv2_greyscale, str2bool, save_h5
13 |
14 |
15 | def get_transforms(color):
16 |
17 | MEAN_IMAGE = 0.443728476019
18 | STD_IMAGE = 0.20197947209
19 |
20 | transform = transforms.Compose([
21 | transforms.Lambda(cv2_greyscale), transforms.Lambda(cv2_scale),
22 | transforms.Lambda(np_reshape), transforms.ToTensor(),
23 | transforms.Normalize((MEAN_IMAGE, ), (STD_IMAGE, ))
24 | ])
25 |
26 | return transform
27 |
28 |
29 | def remove_option(parser, arg):
30 | for action in parser._actions:
31 | if (vars(action)['option_strings']
32 | and vars(action)['option_strings'][0] == arg) \
33 | or vars(action)['dest'] == arg:
34 | parser._remove_action(action)
35 |
36 | for action in parser._action_groups:
37 | vars_action = vars(action)
38 | var_group_actions = vars_action['_group_actions']
39 | for x in var_group_actions:
40 | if x.dest == arg:
41 | var_group_actions.remove(x)
42 | return
43 |
44 |
45 | if __name__ == '__main__':
46 | parser = argparse.ArgumentParser()
47 | parser.add_argument(
48 | "--dataset_path",
49 | default=os.path.join('..', 'benchmark-patches-8k'),
50 | type=str,
51 | help='Path to the pre-generated patches')
52 | parser.add_argument(
53 | "--mrSize", default=12.0, type=float,
54 | help=' patch size in image is mrSize * pt.size. Default mrSize is 12' )
55 | parser.add_argument(
56 | "--save_path",
57 | default=os.path.join('..', 'benchmark-features'),
58 | type=str,
59 | help='Path to store the features')
60 | parser.add_argument(
61 | "--method_name", default='sift8k_8000_hardnet', type=str)
62 | parser.add_argument(
63 | "--weights_path",
64 | default=os.path.join('third_party', 'hardnet', 'pretrained',
65 | 'train_liberty_with_aug',
66 | 'checkpoint_liberty_with_aug.pth'),
67 | type=str,
68 | help='Path to the model weights')
69 | parser.add_argument(
70 | "--subset",
71 | default='both',
72 | type=str,
73 | help='Options: "val", "test", "both", "spc-fix"')
74 | parser.add_argument(
75 | "--clahe-mode",
76 | default='None',
77 | type=str,
78 | help='can be None, detector, descriptor, both')
79 |
80 | args = parser.parse_args()
81 |
82 | if args.subset not in ['val', 'test', 'both', 'spc-fix']:
83 | raise ValueError('Unknown value for --subset')
84 | seqs = []
85 | if args.subset == 'spc-fix':
86 | seqs += ['st_pauls_cathedral']
87 | else:
88 | if args.subset in ['val', 'both']:
89 | with open(os.path.join('data', 'val.json')) as f:
90 | seqs += json.load(f)
91 | if args.subset in ['test', 'both']:
92 | with open(os.path.join('data', 'test.json')) as f:
93 | seqs += json.load(f)
94 | print('Processing the following scenes: {}'.format(seqs))
95 |
96 |
97 | # Hacky work-around: reset argv for the HardNet argparse
98 | sys.path.append(os.path.join('third_party', 'hardnet', 'code'))
99 | sys.argv = [sys.argv[0]]
100 | from third_party.hardnet.code.HardNet import HardNet
101 | from third_party.hardnet.code.Utils import cv2_scale, np_reshape
102 | import torch
103 | try:
104 | if torch.cuda.is_available():
105 | device = torch.device('cuda:0')
106 | else:
107 | device = torch.device('cpu')
108 | except:
109 | device = torch.device('cpu')
110 |
111 | suffix = ""
112 | if args.clahe_mode.lower() == 'detector':
113 | suffix = "_clahe_det"
114 | elif args.clahe_mode.lower() == 'descriptor':
115 | suffix = "_clahe_desc"
116 | elif args.clahe_mode.lower() == 'both':
117 | suffix = "_clahe_det_desc"
118 | elif args.clahe_mode.lower() == 'none':
119 | pass
120 | else:
121 | raise ValueError("unknown CLAHE mode. Try detector, descriptor or both")
122 |
123 | if abs(args.mrSize - 12.) > 0.1:
124 | suffix+= '_mrSize{:.1f}'.format(args.mrSize)
125 |
126 | args.method_name += suffix
127 | print('Saving descriptors to folder: {}'.format(args.method_name))
128 |
129 | transforms = get_transforms(False)
130 |
131 | model = HardNet()
132 | model.load_state_dict(torch.load(args.weights_path,map_location=device)['state_dict'])
133 | print('Loaded weights: {}'.format(args.weights_path))
134 |
135 | model = model.to(device)
136 | model.eval()
137 |
138 | for idx, seq_name in enumerate(seqs):
139 | print('Processing "{}"'.format(seq_name))
140 |
141 | seq_descriptors = {}
142 | patches_h5py_file = os.path.join(args.dataset_path, seq_name,
143 | 'patches{}.h5'.format(suffix))
144 |
145 | with h5py.File(patches_h5py_file, 'r') as patches_h5py:
146 | for key, patches in tqdm(patches_h5py.items()):
147 | patches = patches.value
148 | bs = 128
149 | descriptors = np.zeros((len(patches), 128))
150 |
151 | for i in range(0, len(patches), bs):
152 | data_a = patches[i:i + bs, :, :, :]
153 | data_a = torch.stack(
154 | [transforms(patch) for patch in data_a]).to(device)
155 | # compute output
156 | with torch.no_grad():
157 | out_a = model(data_a)
158 | descriptors[i:i + bs] = out_a.cpu().detach().numpy()
159 |
160 | seq_descriptors[key] = descriptors.astype(np.float32)
161 | print('Processed {} images: {} descriptors/image'.format(
162 | len(seq_descriptors),
163 | np.array([s.shape[0] for s in seq_descriptors.values()]).mean()))
164 |
165 | cur_path = os.path.join(args.save_path, args.method_name, seq_name)
166 | if not os.path.exists(cur_path):
167 | os.makedirs(cur_path)
168 | save_h5(seq_descriptors, os.path.join(cur_path, 'descriptors.h5'))
169 |
170 | sub_files_in = ['keypoints{}.h5'.format(suffix), 'scales{}.h5'.format(suffix), 'angles{}.h5'.format(suffix), 'scores{}.h5'.format(suffix)]
171 | sub_files_out = ['keypoints.h5', 'scales.h5', 'angles.h5', 'scores.h5']
172 |
173 | for sub_file_in, sub_file_out in zip(sub_files_in, sub_files_out):
174 | shutil.copyfile(
175 | os.path.join(args.dataset_path, seq_name, sub_file_in),
176 | os.path.join(cur_path, sub_file_out))
177 |
178 | print('Done sequence: {}'.format(seq_name))
179 |
--------------------------------------------------------------------------------
/extract_descriptors_jit.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import numpy as np
3 | import argparse
4 | import h5py
5 | from tqdm import tqdm
6 | import os
7 | import sys
8 | import shutil
9 | import json
10 |
11 | import torchvision.transforms as transforms
12 | from utils import cv2_greyscale, str2bool, save_h5
13 |
14 |
15 | def get_transforms(color):
16 |
17 | MEAN_IMAGE = 0.443728476019
18 | STD_IMAGE = 0.20197947209
19 |
20 | transform = transforms.Compose([
21 | transforms.Lambda(cv2_greyscale),# transforms.Lambda(cv2_scale),
22 | #transforms.Lambda(np_reshape),
23 | transforms.ToTensor(),
24 | transforms.Normalize((MEAN_IMAGE, ), (STD_IMAGE, ))
25 | ])
26 |
27 | return transform
28 |
29 |
30 | def remove_option(parser, arg):
31 | for action in parser._actions:
32 | if (vars(action)['option_strings']
33 | and vars(action)['option_strings'][0] == arg) \
34 | or vars(action)['dest'] == arg:
35 | parser._remove_action(action)
36 |
37 | for action in parser._action_groups:
38 | vars_action = vars(action)
39 | var_group_actions = vars_action['_group_actions']
40 | for x in var_group_actions:
41 | if x.dest == arg:
42 | var_group_actions.remove(x)
43 | return
44 |
45 |
46 | if __name__ == '__main__':
47 | parser = argparse.ArgumentParser()
48 | parser.add_argument(
49 | "--dataset_path",
50 | default=os.path.join('..', 'benchmark-patches-8k'),
51 | type=str,
52 | help='Path to the pre-generated patches')
53 | parser.add_argument(
54 | "--mrSize", default=12.0, type=float,
55 | help=' patch size in image is mrSize * pt.size. Default mrSize is 12' )
56 | parser.add_argument(
57 | "--save_path",
58 | default=os.path.join('..', 'benchmark-features'),
59 | type=str,
60 | help='Path to store the features')
61 | parser.add_argument(
62 | "--method_name", default='sift8k_8000_hardnet', type=str)
63 | parser.add_argument(
64 | "--weights_path",
65 | default=os.path.join('third_party', 'hardnet', 'pretrained',
66 | 'train_liberty_with_aug',
67 | 'checkpoint_liberty_with_aug.pth'),
68 | type=str,
69 | help='Path to the model weights')
70 | parser.add_argument(
71 | "--subset",
72 | default='both',
73 | type=str,
74 | help='Options: "val", "test", "both", "spc-fix"')
75 | parser.add_argument(
76 | "--clahe-mode",
77 | default='None',
78 | type=str,
79 | help='can be None, detector, descriptor, both')
80 | parser.add_argument(
81 | "--patchSize",
82 | default=32,
83 | type=int,
84 | help=' patch size in pixels. Default 32')
85 |
86 | args = parser.parse_args()
87 |
88 | if args.subset not in ['val', 'test', 'both', 'spc-fix']:
89 | raise ValueError('Unknown value for --subset')
90 | seqs = []
91 | if args.subset == 'spc-fix':
92 | seqs += ['st_pauls_cathedral']
93 | else:
94 | if args.subset in ['val', 'both']:
95 | with open(os.path.join('data', 'val.json')) as f:
96 | seqs += json.load(f)
97 | if args.subset in ['test', 'both']:
98 | with open(os.path.join('data', 'test.json')) as f:
99 | seqs += json.load(f)
100 | print('Processing the following scenes: {}'.format(seqs))
101 |
102 |
103 | # Hacky work-around: reset argv for the HardNet argparse
104 | sys.path.append(os.path.join('third_party', 'hardnet', 'code'))
105 | sys.argv = [sys.argv[0]]
106 | #from third_party.hardnet.code.HardNet import HardNet
107 | from third_party.hardnet.code.Utils import cv2_scale, np_reshape
108 | import torch
109 | try:
110 | if torch.cuda.is_available():
111 | device = torch.device('cuda:0')
112 | else:
113 | device = torch.device('cpu')
114 | except:
115 | device = torch.device('cpu')
116 |
117 | suffix = ""
118 | if args.clahe_mode.lower() == 'detector':
119 | suffix = "_clahe_det"
120 | elif args.clahe_mode.lower() == 'descriptor':
121 | suffix = "_clahe_desc"
122 | elif args.clahe_mode.lower() == 'both':
123 | suffix = "_clahe_det_desc"
124 | elif args.clahe_mode.lower() == 'none':
125 | pass
126 | else:
127 | raise ValueError("unknown CLAHE mode. Try detector, descriptor or both")
128 |
129 | if abs(args.mrSize - 12.) > 0.1:
130 | suffix+= '_mrSize{:.1f}'.format(args.mrSize)
131 | assert(args.patchSize > 0)
132 | if args.patchSize != 32:
133 | suffix += '_patchSize{}'.format(args.patchSize)
134 |
135 | args.method_name += suffix
136 | print('Saving descriptors to folder: {}'.format(args.method_name))
137 |
138 | transforms = get_transforms(False)
139 |
140 | #model = HardNet()
141 | #try:
142 | # model.load_state_dict(torch.load(args.weights_path,map_location=device)['state_dict'])
143 | #except:
144 | # model.load_state_dict(torch.load(args.weights_path,map_location=device))
145 | model = torch.jit.load(args.weights_path)#.cuda()
146 | print('Loaded weights: {}'.format(args.weights_path))
147 | model = model.to(device)
148 | model.eval()
149 |
150 | for idx, seq_name in enumerate(seqs):
151 | print('Processing "{}"'.format(seq_name))
152 |
153 | seq_descriptors = {}
154 | patches_h5py_file = os.path.join(args.dataset_path, seq_name,
155 | 'patches{}.h5'.format(suffix))
156 |
157 | with h5py.File(patches_h5py_file, 'r') as patches_h5py:
158 | for key, patches in tqdm(patches_h5py.items()):
159 | patches = patches.value
160 | bs = 128
161 | descriptors = np.zeros((len(patches), 128))
162 | for i in range(0, len(patches), bs):
163 | data_a = patches[i:i + bs, :, :, :]
164 | data_a = torch.stack(
165 | [transforms(patch) for patch in data_a]).to(device)
166 | # compute output
167 | with torch.no_grad():
168 | out_a = model(data_a)
169 | descriptors[i:i + bs] = out_a.cpu().detach().numpy()
170 |
171 | seq_descriptors[key] = descriptors.astype(np.float32)
172 | print('Processed {} images: {} descriptors/image'.format(
173 | len(seq_descriptors),
174 | np.array([s.shape[0] for s in seq_descriptors.values()]).mean()))
175 |
176 | cur_path = os.path.join(args.save_path, args.method_name, seq_name)
177 | if not os.path.exists(cur_path):
178 | os.makedirs(cur_path)
179 | save_h5(seq_descriptors, os.path.join(cur_path, 'descriptors.h5'))
180 |
181 | sub_files_in = ['keypoints{}.h5'.format(suffix), 'scales{}.h5'.format(suffix), 'angles{}.h5'.format(suffix), 'scores{}.h5'.format(suffix)]
182 | sub_files_out = ['keypoints.h5', 'scales.h5', 'angles.h5', 'scores.h5']
183 |
184 | for sub_file_in, sub_file_out in zip(sub_files_in, sub_files_out):
185 | shutil.copyfile(
186 | os.path.join(args.dataset_path, seq_name, sub_file_in),
187 | os.path.join(cur_path, sub_file_out))
188 |
189 | print('Done sequence: {}'.format(seq_name))
190 |
--------------------------------------------------------------------------------
/extract_descriptors_kornia.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import numpy as np
3 | import argparse
4 | import h5py
5 | from tqdm import tqdm
6 | import os
7 | import sys
8 | import shutil
9 | import json
10 |
11 | import torchvision.transforms as transforms
12 | from utils import cv2_greyscale, str2bool, save_h5
13 |
14 |
15 | def get_transforms(color):
16 |
17 | MEAN_IMAGE = 0.443728476019
18 | STD_IMAGE = 0.20197947209
19 |
20 | transform = transforms.Compose([
21 | transforms.Lambda(cv2_greyscale),#, transforms.Lambda(cv2_scale),
22 | #transforms.Lambda(np_reshape),
23 | transforms.ToTensor(),
24 | transforms.Normalize((MEAN_IMAGE, ), (STD_IMAGE, ))
25 | ])
26 |
27 | return transform
28 |
29 |
30 | def remove_option(parser, arg):
31 | for action in parser._actions:
32 | if (vars(action)['option_strings']
33 | and vars(action)['option_strings'][0] == arg) \
34 | or vars(action)['dest'] == arg:
35 | parser._remove_action(action)
36 |
37 | for action in parser._action_groups:
38 | vars_action = vars(action)
39 | var_group_actions = vars_action['_group_actions']
40 | for x in var_group_actions:
41 | if x.dest == arg:
42 | var_group_actions.remove(x)
43 | return
44 |
45 |
46 | if __name__ == '__main__':
47 | parser = argparse.ArgumentParser()
48 | parser.add_argument(
49 | "--dataset_path",
50 | default=os.path.join('..', 'benchmark-patches-8k'),
51 | type=str,
52 | help='Path to the pre-generated patches')
53 | parser.add_argument(
54 | "--mrSize", default=12.0, type=float,
55 | help=' patch size in image is mrSize * pt.size. Default mrSize is 12' )
56 | parser.add_argument(
57 | "--save_path",
58 | default=os.path.join('..', 'benchmark-features'),
59 | type=str,
60 | help='Path to store the features')
61 | parser.add_argument(
62 | "--method_name", default='sift8k_8000_', type=str)
63 | parser.add_argument(
64 | "--desc_name",
65 | default='tfeat',
66 | type=str,
67 | help='Options: tfeat, sosnet, hardnet, mkd')
68 | parser.add_argument(
69 | "--subset",
70 | default='both',
71 | type=str,
72 | help='Options: "val", "test", "both", "spc-fix"')
73 | parser.add_argument(
74 | "--clahe-mode",
75 | default='None',
76 | type=str,
77 | help='can be None, detector, descriptor, both')
78 |
79 | args = parser.parse_args()
80 | args.desc_name = args.desc_name.lower()
81 | if args.subset not in ['val', 'test', 'train', 'both', 'spc-fix']:
82 | raise ValueError('Unknown value for --subset')
83 | if args.desc_name not in ['tfeat', 'sosnet', 'hardnet', 'mkd']:
84 | raise ValueError('Unknown value for --desc_name')
85 | seqs = []
86 | if args.subset == 'spc-fix':
87 | seqs += ['st_pauls_cathedral']
88 | else:
89 | if args.subset in ['val', 'both']:
90 | with open(os.path.join('data', 'val.json')) as f:
91 | seqs += json.load(f)
92 | if args.subset in ['train']:
93 | with open(os.path.join('data', 'train.json')) as f:
94 | seqs += json.load(f)
95 | if args.subset in ['test', 'both']:
96 | with open(os.path.join('data', 'test.json')) as f:
97 | seqs += json.load(f)
98 | print('Processing the following scenes: {}'.format(seqs))
99 |
100 |
101 | import torch
102 | import kornia.feature as KF
103 | try:
104 | if torch.cuda.is_available():
105 | device = torch.device('cuda:0')
106 | else:
107 | device = torch.device('cpu')
108 | except:
109 | device = torch.device('cpu')
110 |
111 | suffix = ""
112 | if args.clahe_mode.lower() == 'detector':
113 | suffix = "_clahe_det"
114 | elif args.clahe_mode.lower() == 'descriptor':
115 | suffix = "_clahe_desc"
116 | elif args.clahe_mode.lower() == 'both':
117 | suffix = "_clahe_det_desc"
118 | elif args.clahe_mode.lower() == 'none':
119 | pass
120 | else:
121 | raise ValueError("unknown CLAHE mode. Try detector, descriptor or both")
122 |
123 | if abs(args.mrSize - 12.) > 0.1:
124 | suffix+= '_mrSize{:.1f}'.format(args.mrSize)
125 |
126 | args.method_name += args.desc_name
127 | args.method_name += suffix
128 | print('Saving descriptors to folder: {}'.format(args.method_name))
129 |
130 | transforms = get_transforms(False)
131 | if args.desc_name == 'tfeat':
132 | model = KF.TFeat(True)
133 | elif args.desc_name == 'hardnet':
134 | model = KF.HardNet(True)
135 | elif args.desc_name == 'sosnet':
136 | model = KF.SOSNet(True)
137 | elif args.desc_name == 'mkd':
138 | model = KF.MKDDescriptor(32)
139 | else:
140 | sys.exit(1)
141 | model = model.to(device)
142 | model.eval()
143 |
144 | for idx, seq_name in enumerate(seqs):
145 | print('Processing "{}"'.format(seq_name))
146 |
147 | seq_descriptors = {}
148 | patches_h5py_file = os.path.join(args.dataset_path, seq_name,
149 | 'patches{}.h5'.format(suffix))
150 |
151 | with h5py.File(patches_h5py_file, 'r') as patches_h5py:
152 | for key, patches in tqdm(patches_h5py.items()):
153 | patches = patches.value
154 | bs = 128
155 | descriptors = np.zeros((len(patches), 128))
156 |
157 | for i in range(0, len(patches), bs):
158 | data_a = patches[i:i + bs, :, :, :]
159 | data_a = torch.stack(
160 | [transforms(patch) for patch in data_a]).to(device)
161 | # compute output
162 | with torch.no_grad():
163 | out_a = model(data_a)
164 | descriptors[i:i + bs] = out_a.cpu().detach().numpy()
165 |
166 | seq_descriptors[key] = descriptors.astype(np.float32)
167 | print('Processed {} images: {} descriptors/image'.format(
168 | len(seq_descriptors),
169 | np.array([s.shape[0] for s in seq_descriptors.values()]).mean()))
170 |
171 | cur_path = os.path.join(args.save_path, args.method_name, seq_name)
172 | if not os.path.exists(cur_path):
173 | os.makedirs(cur_path)
174 | save_h5(seq_descriptors, os.path.join(cur_path, 'descriptors.h5'))
175 |
176 | sub_files_in = ['keypoints{}.h5'.format(suffix), 'scales{}.h5'.format(suffix), 'angles{}.h5'.format(suffix), 'scores{}.h5'.format(suffix)]
177 | sub_files_out = ['keypoints.h5', 'scales.h5', 'angles.h5', 'scores.h5']
178 |
179 | for sub_file_in, sub_file_out in zip(sub_files_in, sub_files_out):
180 | shutil.copyfile(
181 | os.path.join(args.dataset_path, seq_name, sub_file_in),
182 | os.path.join(cur_path, sub_file_out))
183 |
184 | print('Done sequence: {}'.format(seq_name))
185 |
--------------------------------------------------------------------------------
/extract_descriptors_l2net.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import numpy as np
3 | import argparse
4 | import h5py
5 | from tqdm import tqdm
6 | import os
7 | import sys
8 | import shutil
9 | import json
10 | import scipy.io as sio
11 |
12 | import torchvision.transforms as transforms
13 | from utils import cv2_greyscale, str2bool, save_h5
14 |
15 | def get_transforms():
16 |
17 | transform = transforms.Compose([
18 | transforms.Lambda(cv2_greyscale),
19 | transforms.Lambda(cv2_scale),
20 | transforms.Lambda(np_reshape)
21 |
22 | ])
23 |
24 | return transform
25 |
26 | def remove_option(parser, arg):
27 | for action in parser._actions:
28 | if (vars(action)['option_strings']
29 | and vars(action)['option_strings'][0] == arg) \
30 | or vars(action)['dest'] == arg:
31 | parser._remove_action(action)
32 |
33 | for action in parser._action_groups:
34 | vars_action = vars(action)
35 | var_group_actions = vars_action['_group_actions']
36 | for x in var_group_actions:
37 | if x.dest == arg:
38 | var_group_actions.remove(x)
39 | return
40 |
41 |
42 | if __name__ == '__main__':
43 | parser = argparse.ArgumentParser()
44 | parser.add_argument(
45 | "--dataset_path",
46 | default=os.path.join('..', 'benchmark-patches-8k'),
47 | type=str,
48 | help='Path to the pre-generated patches')
49 | parser.add_argument(
50 | "--mrSize", default=12.0, type=float,
51 | help=' patch size in image is mrSize * pt.size. Default mrSize is 12' )
52 | parser.add_argument(
53 | "--save_path",
54 | default=os.path.join('..', 'benchmark-features'),
55 | type=str,
56 | help='Path to store the features')
57 | parser.add_argument(
58 | "--method_name", default='sift8k_8000_l2net', type=str)
59 | parser.add_argument(
60 | "--weights_path",
61 | default=os.path.join('third_party', 'l2net-config', 'l2net_ported_weights_lib+.pth'),
62 | type=str,
63 | help='Path to the model weights')
64 | parser.add_argument(
65 | "--matlab_weights_path",
66 | default=os.path.join('third_party', 'l2net', 'matlab', 'L2Net-LIB+.mat'),
67 | type=str,
68 | help='Path to the model weights')
69 | parser.add_argument(
70 | "--subset",
71 | default='both',
72 | type=str,
73 | help='Options: "val", "test", "both", "spc-fix"')
74 | parser.add_argument(
75 | "--clahe-mode",
76 | default='None',
77 | type=str,
78 | help='can be None, detector, descriptor, both')
79 |
80 | args = parser.parse_args()
81 |
82 | if args.subset not in ['val', 'test', 'both', 'spc-fix']:
83 | raise ValueError('Unknown value for --subset')
84 | seqs = []
85 | if args.subset == 'spc-fix':
86 | seqs += ['st_pauls_cathedral']
87 | else:
88 | if args.subset in ['val', 'both']:
89 | with open(os.path.join('data', 'val.json')) as f:
90 | seqs += json.load(f)
91 | if args.subset in ['test', 'both']:
92 | with open(os.path.join('data', 'test.json')) as f:
93 | seqs += json.load(f)
94 | print('Processing the following scenes: {}'.format(seqs))
95 |
96 |
97 | # Hacky work-around: reset argv for the HardNet argparse
98 | sys.path.append(os.path.join('misc', 'l2net'))
99 | sys.argv = [sys.argv[0]]
100 | from misc.l2net.l2net_model import L2Net
101 | from third_party.hardnet.code.Utils import cv2_scale, np_reshape
102 | import torch
103 |
104 |
105 | try:
106 | if torch.cuda.is_available():
107 | device = torch.device('cuda:0')
108 | else:
109 | device = torch.device('cpu')
110 | except:
111 | device = torch.device('cpu')
112 |
113 | suffix = ""
114 | if args.clahe_mode.lower() == 'detector':
115 | suffix = "_clahe_det"
116 | elif args.clahe_mode.lower() == 'descriptor':
117 | suffix = "_clahe_desc"
118 | elif args.clahe_mode.lower() == 'both':
119 | suffix = "_clahe_det_desc"
120 | elif args.clahe_mode.lower() == 'none':
121 | pass
122 | else:
123 | raise ValueError("unknown CLAHE mode. Try detector, descriptor or both")
124 |
125 | if abs(args.mrSize - 12.) > 0.1:
126 | suffix+= '_mrSize{:.1f}'.format(args.mrSize)
127 | args.method_name+=suffix
128 | print('Saving descriptors to folder: {}'.format(args.method_name))
129 |
130 | # get pre-trained image mean
131 | l2net_weights = sio.loadmat(args.matlab_weights_path)
132 | imgMean = l2net_weights['pixMean']
133 |
134 | transforms = get_transforms()
135 |
136 | model = L2Net()
137 | model.load_state_dict(torch.load(args.weights_path,map_location=device))
138 | print('Loaded weights: {}'.format(args.weights_path))
139 |
140 | model = model.to(device)
141 | model.eval()
142 |
143 | for idx, seq_name in enumerate(seqs):
144 | print('Processing "{}"'.format(seq_name))
145 |
146 | seq_descriptors = {}
147 | patches_h5py_file = os.path.join(args.dataset_path, seq_name,
148 | 'patches{}.h5'.format(suffix))
149 |
150 | with h5py.File(patches_h5py_file, 'r') as patches_h5py:
151 | for key, patches in tqdm(patches_h5py.items()):
152 | patches = patches.value
153 |
154 | bs = 128
155 | descriptors = np.zeros((len(patches), 128))
156 |
157 | for i in range(0, len(patches), bs):
158 | data_a = patches[i:i + bs, :, :, :]
159 | data_a = torch.stack([torch.from_numpy(transforms(patch)).squeeze() for patch in data_a]).\
160 | unsqueeze(1).float().to(device)
161 | # compute output
162 | data_a = data_a - torch.from_numpy(imgMean).to(device)
163 | with torch.no_grad():
164 | out_a = model(data_a)
165 | descriptors[i:i + bs] = out_a.cpu().detach().numpy()
166 |
167 | seq_descriptors[key] = descriptors.astype(np.float32)
168 | print('Processed {} images: {} descriptors/image'.format(
169 | len(seq_descriptors),
170 | np.array([s.shape[0] for s in seq_descriptors.values()]).mean()))
171 |
172 | cur_path = os.path.join(args.save_path, args.method_name, seq_name)
173 | if not os.path.exists(cur_path):
174 | os.makedirs(cur_path)
175 | save_h5(seq_descriptors, os.path.join(cur_path, 'descriptors.h5'))
176 |
177 | sub_files_in = ['keypoints{}.h5'.format(suffix), 'scales{}.h5'.format(suffix), 'angles{}.h5'.format(suffix), 'scores{}.h5'.format(suffix)]
178 | sub_files_out = ['keypoints.h5', 'scales.h5', 'angles.h5', 'scores.h5']
179 |
180 | for sub_file_in, sub_file_out in zip(sub_files_in, sub_files_out):
181 | shutil.copyfile(
182 | os.path.join(args.dataset_path, seq_name, sub_file_in),
183 | os.path.join(cur_path, sub_file_out))
184 |
185 | print('Done sequence: {}'.format(seq_name))
186 |
--------------------------------------------------------------------------------
/extract_descriptors_logpolar.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import numpy as np
3 | import argparse
4 | import h5py
5 | from tqdm import tqdm
6 | import os
7 | import sys
8 | import cv2
9 | from utils import str2bool, save_h5
10 | import shutil
11 | import json
12 |
13 |
14 | def l_clahe(img):
15 | clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
16 | lab = cv2.cvtColor(img, cv2.COLOR_RGB2Lab)
17 | lab[:, :, 0] = clahe.apply(lab[:, :, 0])
18 | return cv2.cvtColor(lab, cv2.COLOR_Lab2RGB)
19 |
20 |
21 | if __name__ == '__main__':
22 | parser = argparse.ArgumentParser()
23 |
24 | parser.add_argument(
25 | "--sequences_folder",
26 | default=os.path.join('..', 'imw-2020'),
27 | help="path to config file",
28 | type=str)
29 | parser.add_argument(
30 | "--dataset_path",
31 | default=os.path.join('..', 'benchmark-patches-8k'),
32 | type=str,
33 | help='Path to the pre-generated patches')
34 | parser.add_argument(
35 | "--save_path",
36 | default=os.path.join('..', 'benchmark-features'),
37 | type=str,
38 | help='Path to store the features')
39 | parser.add_argument(
40 | "--method_name", default='sift8k_8000_logpolar96', type=str)
41 | parser.add_argument(
42 | "--config_file",
43 | default='third_party/log_polar_descriptors/configs/init_one_example_ptn_96.yml',
44 | help="path to config file",
45 | type=str)
46 | parser.add_argument(
47 | "--mrSize",
48 | default=12.0,
49 | type=float,
50 | help=' patch size in image is mrSize * pt.size. Default mrSize is 12')
51 | parser.add_argument(
52 | "--subset",
53 | default='both',
54 | type=str,
55 | help='Options: "val", "test", "both", "spc-fix"')
56 | parser.add_argument(
57 | "--clahe-mode",
58 | default='None',
59 | type=str,
60 | help='can be None, detector, descriptor, both')
61 | parser.add_argument(
62 | "opts",
63 | help="Modify config options using the command-line",
64 | default=None,
65 | nargs=argparse.REMAINDER)
66 |
67 | args = parser.parse_args()
68 |
69 | if args.subset not in ['val', 'test', 'both', 'spc-fix']:
70 | raise ValueError('Unknown value for --subset')
71 | seqs = []
72 | if args.subset == 'spc-fix':
73 | seqs += ['st_pauls_cathedral']
74 | else:
75 | if args.subset in ['val', 'both']:
76 | with open(os.path.join('data', 'val.json')) as f:
77 | seqs += json.load(f)
78 | if args.subset in ['test', 'both']:
79 | with open(os.path.join('data', 'test.json')) as f:
80 | seqs += json.load(f)
81 | print('Processing the following scenes: {}'.format(seqs))
82 |
83 | # Hacky work-around: reset argv for the HardNet argparse
84 | sys.path.append(os.path.join('third_party', 'log_polar_descriptors'))
85 | sys.argv = [sys.argv[0]]
86 | from third_party.log_polar_descriptors.configs.defaults import _C as cfg
87 | from third_party.log_polar_descriptors.modules.hardnet.models import HardNet
88 | suffix = ""
89 | if args.clahe_mode.lower() == 'detector':
90 | suffix = "_clahe_det"
91 | elif args.clahe_mode.lower() == 'descriptor':
92 | suffix = "_clahe_desc"
93 | elif args.clahe_mode.lower() == 'both':
94 | suffix = "_clahe_det_desc"
95 | elif args.clahe_mode.lower() == 'none':
96 | pass
97 | else:
98 | raise ValueError(
99 | "unknown CLAHE mode. Try detector, descriptor or both")
100 |
101 | args.method_name += suffix
102 |
103 | print('Saving descriptors to folder: {}'.format(args.method_name))
104 |
105 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
106 | print (device)
107 | num_gpus = int(
108 | os.environ["WORLD_SIZE"]) if "WORLD_SIZE" in os.environ else 1
109 |
110 | if args.config_file != "":
111 | cfg.merge_from_file(args.config_file)
112 | cfg.merge_from_list(args.opts)
113 |
114 | model = HardNet(
115 | transform=cfg.TEST.TRANSFORMER,
116 | coords=cfg.TEST.COORDS,
117 | patch_size=cfg.TEST.IMAGE_SIZE,
118 | scale=cfg.TEST.SCALE,
119 | is_desc256=cfg.TEST.IS_DESC_256,
120 | orientCorrect=cfg.TEST.ORIENT_CORRECTION)
121 |
122 | model.load_state_dict(
123 | torch.load(
124 | os.path.join('third_party', 'log_polar_descriptors',
125 | cfg.TEST.MODEL_WEIGHTS))['state_dict'])
126 | model.eval()
127 | model.to(device)
128 |
129 | for idx, seq_name in enumerate(seqs):
130 |
131 | print('Processing "{}"'.format(seq_name))
132 |
133 | keypoints = h5py.File(
134 | os.path.join(args.dataset_path, seq_name,
135 | 'keypoints{}.h5'.format(suffix)), 'r')
136 | scales = h5py.File(
137 | os.path.join(args.dataset_path, seq_name,
138 | 'scales{}.h5'.format(suffix)), 'r')
139 | angles = h5py.File(
140 | os.path.join(args.dataset_path, seq_name,
141 | 'angles{}.h5'.format(suffix)), 'r')
142 | seq_descriptors = {}
143 | scene_path = os.path.join(args.sequences_folder,
144 | seq_name, 'set_100/images/')
145 | for key, keypoints in tqdm(keypoints.items()):
146 | img = cv2.imread(
147 | os.path.join(scene_path, key + '.jpg'))
148 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
149 | if args.clahe_mode.lower() in ['descriptor', 'both']:
150 | img = l_clahe(img)
151 | img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
152 | # pad image and fix keypoints
153 | if img.shape[0] > cfg.TEST.PAD_TO or img.shape[1] > cfg.TEST.PAD_TO:
154 | raise RuntimeError(
155 | "Image {} exceeds acceptable size".format(img.shape))
156 |
157 | fillHeight = cfg.TEST.PAD_TO - img.shape[0]
158 | fillWidth = cfg.TEST.PAD_TO - img.shape[1]
159 |
160 | padLeft = int(np.round(fillWidth / 2))
161 | padRight = int(fillWidth - padLeft)
162 | padUp = int(np.round(fillHeight / 2))
163 | padDown = int(fillHeight - padUp)
164 |
165 | img = np.pad(img,
166 | pad_width=((padUp, padDown), (padLeft, padRight)),
167 | mode='reflect')
168 |
169 | # Iterate over keypoints
170 | keypoint_locations = []
171 | for kpIDX, kp_loc in enumerate(keypoints):
172 | normKp_a = 2 * np.array([[(kp_loc[0] + padLeft) /
173 | (cfg.TEST.PAD_TO),
174 | (kp_loc[1] + padUp) /
175 | (cfg.TEST.PAD_TO)]]) - 1
176 | keypoint_locations.append(normKp_a)
177 |
178 | all_desc = []
179 | bs = 500
180 | for i in range(0, len(keypoint_locations), bs):
181 | oris = np.array([
182 | np.deg2rad(orient) for orient in angles[key][:][i:i + bs]
183 | ])
184 |
185 | theta = [
186 | torch.from_numpy(np.array(
187 | keypoint_locations)[i:i + bs]).float().squeeze(),
188 | torch.from_numpy(scales[key][:][i:i + bs]).float().squeeze(),
189 | torch.from_numpy(oris).float().squeeze()
190 | ]
191 | # due to multiplier during extraction from detect_sift_keypoints...
192 | theta[1] = theta[1]/args.mrSize
193 |
194 | imgs = torch.from_numpy(img).unsqueeze(0).to(device)
195 | img_keypoints = [
196 | theta[0].to(device), theta[1].to(device),
197 | theta[2].to(device)
198 | ]
199 |
200 | # Deal with batches size 1
201 | if len(oris) == 1:
202 | img_keypoints[0] = img_keypoints[0].unsqueeze(0)
203 | img_keypoints[1] = img_keypoints[1].unsqueeze(0)
204 | img_keypoints[2] = img_keypoints[2].unsqueeze(0)
205 |
206 | descriptors, patches = model({
207 | key: imgs
208 | }, img_keypoints, [key] * len(img_keypoints[0]))
209 | all_desc.append(descriptors.data.cpu().numpy())
210 | seq_descriptors[key] = np.vstack(np.array(all_desc)).astype(
211 | np.float32)
212 |
213 | cur_path = os.path.join(args.save_path, args.method_name, seq_name)
214 | if not os.path.exists(cur_path):
215 | os.makedirs(cur_path)
216 | save_h5(seq_descriptors, os.path.join(cur_path, 'descriptors.h5'))
217 | sub_files_in = [
218 | 'keypoints{}.h5'.format(suffix), 'scales{}.h5'.format(suffix),
219 | 'angles{}.h5'.format(suffix), 'scores{}.h5'.format(suffix)
220 | ]
221 | sub_files_out = ['keypoints.h5', 'scales.h5', 'angles.h5', 'scores.h5']
222 |
223 | for sub_file_in, sub_file_out in zip(sub_files_in, sub_files_out):
224 | shutil.copyfile(
225 | os.path.join(args.dataset_path, seq_name, sub_file_in),
226 | os.path.join(cur_path, sub_file_out))
227 |
228 | print('Done sequence: {}'.format(seq_name))
229 |
--------------------------------------------------------------------------------
/extract_descriptors_sosnet.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import torch
3 | import numpy as np
4 | import h5py
5 | from tqdm import tqdm
6 | import os
7 | import sys
8 | import shutil
9 | import json
10 |
11 | from utils import cv2_scale, cv2_greyscale, np_reshape, str2bool, save_h5
12 | from third_party.SOSNet.codes.sosnet_model import SOSNet32x32
13 | import torchvision.transforms as transforms
14 |
15 |
16 | def get_transforms():
17 |
18 | transform = transforms.Compose([
19 | transforms.Lambda(cv2_greyscale), transforms.Lambda(cv2_scale),
20 | transforms.Lambda(np_reshape), transforms.ToTensor()
21 | ])
22 |
23 | return transform
24 |
25 |
26 | if __name__ == '__main__':
27 | parser = argparse.ArgumentParser()
28 | parser.add_argument(
29 | "--dataset_path",
30 | default=os.path.join('..', 'benchmark-patches-8k'),
31 | type=str,
32 | help='Path to the pre-generated patches')
33 | parser.add_argument(
34 | "--save_path",
35 | default=os.path.join('..', 'benchmark-features'),
36 | type=str,
37 | help='Path to store the features')
38 | parser.add_argument(
39 | "--method_name", default='sift8k_8000_sosnet', type=str)
40 | parser.add_argument(
41 | "--weights_path",
42 | default=os.path.join('third_party', 'SOSNet', 'sosnet-weights',
43 | 'sosnet-32x32-liberty.pth'),
44 | type=str,
45 | help='Path to the model weights')
46 | parser.add_argument(
47 | "--subset",
48 | default='both',
49 | type=str,
50 | help='Options: "val", "test", "both", "spc-fix"')
51 | parser.add_argument(
52 | "--clahe-mode",
53 | default='None',
54 | type=str,
55 | help='can be None, detector, descriptor, both')
56 |
57 | args = parser.parse_args()
58 |
59 | if args.subset not in ['val', 'test', 'both', 'spc-fix']:
60 | raise ValueError('Unknown value for --subset')
61 | seqs = []
62 | if args.subset == 'spc-fix':
63 | seqs += ['st_pauls_cathedral']
64 | else:
65 | if args.subset in ['val', 'both']:
66 | with open(os.path.join('data', 'val.json')) as f:
67 | seqs += json.load(f)
68 | if args.subset in ['test', 'both']:
69 | with open(os.path.join('data', 'test.json')) as f:
70 | seqs += json.load(f)
71 | print('Processing the following scenes: {}'.format(seqs))
72 |
73 | suffix = ""
74 | if args.clahe_mode.lower() == 'detector':
75 | suffix = "_clahe_det"
76 | elif args.clahe_mode.lower() == 'descriptor':
77 | suffix = "_clahe_desc"
78 | elif args.clahe_mode.lower() == 'both':
79 | suffix = "_clahe_det_desc"
80 | elif args.clahe_mode.lower() == 'none':
81 | pass
82 | else:
83 | raise ValueError("unknown CLAHE mode. Try detector, descriptor or both")
84 |
85 | args.method_name += suffix
86 |
87 | print('Saving descriptors to folder: {}'.format(args.method_name))
88 |
89 | transforms = get_transforms()
90 |
91 | model = SOSNet32x32()
92 | model.load_state_dict(torch.load(args.weights_path))
93 | print('Loaded weights: {}'.format(args.weights_path))
94 |
95 | model.cuda()
96 | model.eval()
97 |
98 | for idx, seq_name in enumerate(seqs):
99 | print('Processing "{}"'.format(seq_name))
100 |
101 | seq_descriptors = {}
102 | patches_h5py_file = os.path.join(args.dataset_path, seq_name,
103 | 'patches{}.h5'.format(suffix))
104 |
105 | with h5py.File(patches_h5py_file, 'r') as patches_h5py:
106 | for key, patches in tqdm(patches_h5py.items()):
107 | patches = patches.value
108 | bs = 128
109 | descriptors = np.zeros((len(patches), 128))
110 |
111 | for i in range(0, len(patches), bs):
112 | data_a = patches[i:i + bs, :, :, :]
113 | data_a = torch.stack(
114 | [transforms(patch) for patch in data_a]).cuda()
115 | # compute output
116 | with torch.no_grad():
117 | out_a = model(data_a)
118 | descriptors[i:i + bs] = out_a.cpu().detach().numpy()
119 |
120 | seq_descriptors[key] = descriptors.astype(np.float32)
121 |
122 | print('Processed {} images: {} descriptors/image'.format(
123 | len(seq_descriptors),
124 | np.array([s.shape[0] for s in seq_descriptors.values()]).mean()))
125 |
126 | cur_path = os.path.join(args.save_path, args.method_name, seq_name)
127 | if not os.path.exists(cur_path):
128 | os.makedirs(cur_path)
129 | save_h5(seq_descriptors, os.path.join(cur_path, 'descriptors.h5'))
130 |
131 | sub_files_in = ['keypoints{}.h5'.format(suffix), 'scales{}.h5'.format(suffix), 'angles{}.h5'.format(suffix), 'scores{}.h5'.format(suffix)]
132 | sub_files_out = ['keypoints.h5', 'scales.h5', 'angles.h5', 'scores.h5']
133 |
134 | for sub_file_in, sub_file_out in zip(sub_files_in, sub_files_out):
135 | shutil.copyfile(
136 | os.path.join(args.dataset_path, seq_name, sub_file_in),
137 | os.path.join(cur_path, sub_file_out))
138 |
139 | print('Done sequence: {}'.format(seq_name))
140 |
--------------------------------------------------------------------------------
/extract_kp2d_features.py:
--------------------------------------------------------------------------------
1 | import os
2 | import h5py
3 | from tqdm import tqdm
4 | import matplotlib.pyplot as plt
5 | import numpy as np
6 | import cv2
7 | import torch
8 | import torch.nn.functional as F
9 | import argparse
10 | from utils import str2bool, save_h5
11 | import sys
12 | sys.path.insert(0, f'{os.getcwd()}/third_party/KP2D')
13 |
14 | import kornia as K
15 | from kp2d.datasets.patches_dataset import PatchesDataset
16 | from kp2d.evaluation.evaluate import evaluate_keypoint_net
17 | from kp2d.networks.keypoint_net import KeypointNet
18 | from kp2d.networks.keypoint_resnet import KeypointResnet
19 |
20 | def convert_imc(kps, resps):
21 | keypoints = kps.reshape(-1, 2).detach().cpu().numpy()
22 | nkp = len(keypoints)
23 | scales = np.ones((nkp, 1)).astype(np.float32)
24 | angles = np.zeros((nkp, 1)).astype(np.float32)
25 | responses = resps.detach().reshape(-1, 1).cpu().numpy()
26 | return keypoints, scales, angles, responses
27 |
28 |
29 | def extract_features(img_fname, keypoint_net, device, MAX_KP, max_size, norm_desc):
30 | img = cv2.cvtColor(cv2.imread(img_fname), cv2.COLOR_BGR2RGB)
31 | timg = K.image_to_tensor(img, False).float()/255.
32 | timg = timg.to(device)
33 | #timg_gray = K.color.rgb_to_grayscale(timg)
34 | H, W = timg.shape[2:]
35 | if max_size>0:
36 | if max_size % 16 != 0:
37 | max_size = int(max_size - (max_size % 16))
38 | min_size = int(min(H, W) * max_size / float(max(H, W)))
39 | if min_size % 16 !=0:
40 | min_size = int(min_size - (min_size % 16))
41 | if H > W:
42 | out_size = (max_size, min_size)
43 | else:
44 | out_size = (min_size, max_size)
45 | with torch.no_grad():
46 | timg_res = K.geometry.resize(timg, out_size)
47 | else:
48 | timg_res = timg
49 | with torch.no_grad():
50 | H2, W2 = timg_res.shape[2:]
51 | coef_h = (H/float(H2))
52 | coef_w = (W/float(W2))
53 | score_1, coord_1, desc1 = keypoint_net(timg_res)
54 | coord_1 = coord_1.permute(0, 2, 3, 1).reshape(-1, 2)
55 | desc1 = desc1.permute(0, 2, 3, 1).reshape(-1, 256)
56 | if norm_desc:
57 | desc1 = F.normalize(desc1, dim=1, p=2)
58 | score_1 = score_1.reshape(-1)
59 | sorted_sc, indices = torch.sort(score_1, descending=True)
60 | idxs = indices[:MAX_KP]
61 | resps = score_1[idxs]
62 | kps = coord_1[idxs]
63 | kps[:, 0] *= coef_w
64 | kps[:, 1] *= coef_h
65 | descs = desc1[idxs]
66 | return kps.reshape(-1, 2), resps, descs
67 |
68 | if __name__ == '__main__':
69 | parser = argparse.ArgumentParser()
70 | parser.add_argument(
71 | "--datasets_folder",
72 | default=os.path.join('..', 'imw-2020'),
73 | help="path to datasets folder",
74 | type=str)
75 | parser.add_argument(
76 | '--num_kp',
77 | type=int,
78 | default=2048,
79 | help='Detector confidence threshold (default: 0.015).')
80 | parser.add_argument(
81 | '--resize_image_to',
82 | type=int,
83 | default=1024,
84 | help='Resize the largest image dimension to this value (default: 1024, '
85 | '0 does nothing).')
86 | parser.add_argument(
87 | '--model_version',
88 | type=str,
89 | default='v4',
90 | choices=["v0", "v1", "v2", "v3", "v4"])
91 | parser.add_argument(
92 | '--device',
93 | type=str,
94 | default='cpu',
95 | choices=["cpu", 'cuda', 'mps']
96 | )
97 | parser.add_argument(
98 | "--save_path",
99 | default=os.path.join('..', 'benchmark-features'),
100 | type=str,
101 | help='Path to store the features')
102 | parser.add_argument(
103 | "--method_name", default='kp2d', type=str)
104 | parser.add_argument(
105 | "--dataset",
106 | default='all',
107 | type=str,
108 | choices=["all", "phototourism", "pragueparks"])
109 | parser.add_argument(
110 | "--norm_desc",
111 | default=False,
112 | type=str2bool,
113 | help='L2Norm of descriptors')
114 | opt, unparsed = parser.parse_known_args()
115 | print(opt)
116 | vv = opt.model_version
117 | device = torch.device(opt.device)
118 | checkpoint = torch.load(f'third_party/KP2D/data/models/kp2d/{vv}.ckpt',
119 | map_location=device)
120 | model_args = checkpoint['config']['model']['params']
121 | keypoint_net = KeypointNet()
122 | keypoint_net.load_state_dict(checkpoint['state_dict'])
123 | keypoint_net.eval()
124 | keypoint_net=keypoint_net.to(device)
125 | INPUT_DIR = opt.datasets_folder
126 | modelname = f'{opt.method_name}_{opt.model_version}'
127 | if opt.norm_desc:
128 | modelname+='_norm'
129 | if opt.resize_image_to > 0:
130 | modelname+= f'_{opt.resize_image_to}'
131 | else:
132 | modelname+= f'_fullres'
133 | OUT_DIR = os.path.join(opt.save_path, modelname)
134 | os.makedirs(OUT_DIR, exist_ok=True)
135 | print (f"Will save to {OUT_DIR}")
136 | if opt.dataset == 'all':
137 | datasets = ['phototourism', 'pragueparks']#[x for x in os.listdir(INPUT_DIR) if (os.path.isdir(os.path.join(INPUT_DIR, x)))]
138 | else:
139 | datasets = [opt.dataset]
140 | for ds in datasets:
141 | ds_in_path = os.path.join(INPUT_DIR, ds)
142 | ds_out_path = os.path.join(OUT_DIR, ds)
143 | os.makedirs(ds_out_path, exist_ok=True)
144 | seqs = [x for x in os.listdir(ds_in_path) if os.path.isdir(os.path.join(ds_in_path, x))]
145 | for seq in seqs:
146 | print (seq)
147 | if os.path.isdir(os.path.join(ds_in_path, seq, 'set_100')):
148 | seq_in_path = os.path.join(ds_in_path, seq, 'set_100', 'images')
149 | else:
150 | seq_in_path = os.path.join(ds_in_path, seq)
151 | seq_out_path = os.path.join(ds_out_path, seq)
152 | os.makedirs(seq_out_path, exist_ok=True)
153 | img_fnames = os.listdir(seq_in_path)
154 | num_kp = []
155 | with h5py.File(f'{seq_out_path}/keypoints.h5', mode='w') as f_kp, \
156 | h5py.File(f'{seq_out_path}/descriptors.h5', mode='w') as f_desc, \
157 | h5py.File(f'{seq_out_path}/scores.h5', mode='w') as f_score, \
158 | h5py.File(f'{seq_out_path}/angles.h5', mode='w') as f_ang, \
159 | h5py.File(f'{seq_out_path}/scales.h5', mode='w') as f_scale:
160 | for img_fname in tqdm(img_fnames):
161 | img_fname_full = os.path.join(seq_in_path, img_fname)
162 | key = os.path.splitext(os.path.basename(img_fname))[0]
163 | kps, resps, descs = extract_features(img_fname_full, keypoint_net, device,
164 | opt.num_kp,
165 | opt.resize_image_to,
166 | opt.norm_desc)
167 | keypoints, scales, angles, responses = convert_imc(kps, resps)
168 | f_desc[key] = descs.reshape(-1, 256).detach().cpu().numpy()
169 | f_score[key] = responses
170 | f_ang[key] = angles
171 | f_kp[key] = keypoints
172 | f_scale[key] = scales
173 | num_kp.append(len(keypoints))
174 | print(f'Finished processing "{ds}/{seq}" -> {np.array(num_kp).mean()} features/image')
175 | print (f"Result is saved to {OUT_DIR}")
176 |
--------------------------------------------------------------------------------
/extract_lanet.py:
--------------------------------------------------------------------------------
1 | import os
2 | import h5py
3 | from tqdm import tqdm
4 | import matplotlib.pyplot as plt
5 | import numpy as np
6 | import cv2
7 | import torch
8 | import torch.nn.functional as F
9 | import argparse
10 | import sys
11 |
12 | def str2bool(v):
13 | if v.lower() in ('yes', 'true', 't', 'y', '1'):
14 | return True
15 | elif v.lower() in ('no', 'false', 'f', 'n', '0'):
16 | return False
17 |
18 | def save_h5(dict_to_save, filename):
19 | """Saves dictionary to hdf5 file"""
20 |
21 | with h5py.File(filename, 'w') as f:
22 | for key in dict_to_save:
23 | f.create_dataset(key, data=dict_to_save[key])
24 |
25 | sys.path.insert(0, f'{os.getcwd()}/third_party/lanet')
26 |
27 | import kornia as K
28 | from network_v1.model import PointModel as PointModel_v1
29 | from network_v0.model import PointModel as PointModel_v0
30 |
31 | def convert_imc(kps, resps):
32 | keypoints = kps.reshape(-1, 2).detach().cpu().numpy()
33 | nkp = len(keypoints)
34 | scales = np.ones((nkp, 1)).astype(np.float32)
35 | angles = np.zeros((nkp, 1)).astype(np.float32)
36 | responses = resps.detach().reshape(-1, 1).cpu().numpy()
37 | return keypoints, scales, angles, responses
38 |
39 |
40 | def extract_features(img_fname, keypoint_net, device, MAX_KP, max_size, norm_desc):
41 | img = cv2.cvtColor(cv2.imread(img_fname), cv2.COLOR_BGR2RGB)
42 | timg = K.image_to_tensor(img, False).float()/255.
43 | timg = timg.to(device)
44 | #timg_gray = K.color.rgb_to_grayscale(timg)
45 | H, W = timg.shape[2:]
46 | if max_size>0:
47 | if max_size % 16 != 0:
48 | max_size = int(max_size - (max_size % 16))
49 | min_size = int(min(H, W) * max_size / float(max(H, W)))
50 | if min_size % 16 !=0:
51 | min_size = int(min_size - (min_size % 16))
52 | if H > W:
53 | out_size = (max_size, min_size)
54 | else:
55 | out_size = (min_size, max_size)
56 | with torch.no_grad():
57 | timg_res = K.geometry.resize(timg, out_size)
58 | else:
59 | timg_res = timg
60 | with torch.no_grad():
61 | H2, W2 = timg_res.shape[2:]
62 | coef_h = (H/float(H2))
63 | coef_w = (W/float(W2))
64 | score_1, coord_1, desc1 = keypoint_net(timg_res)
65 | coord_1 = coord_1.permute(0, 2, 3, 1).reshape(-1, 2)
66 | desc1 = desc1.permute(0, 2, 3, 1).reshape(-1, 256)
67 | if norm_desc:
68 | desc1 = F.normalize(desc1, dim=1, p=2)
69 | score_1 = score_1.reshape(-1)
70 | sorted_sc, indices = torch.sort(score_1, descending=True)
71 | idxs = indices[:MAX_KP]
72 | resps = score_1[idxs]
73 | kps = coord_1[idxs]
74 | kps[:, 0] *= coef_w
75 | kps[:, 1] *= coef_h
76 | descs = desc1[idxs]
77 | return kps.reshape(-1, 2), resps, descs
78 |
79 | if __name__ == '__main__':
80 | parser = argparse.ArgumentParser()
81 | parser.add_argument(
82 | "--datasets_folder",
83 | default=os.path.join('..', 'imw-2020'),
84 | help="path to datasets folder",
85 | type=str)
86 | parser.add_argument(
87 | '--num_kp',
88 | type=int,
89 | default=2048,
90 | help='number of keypoints')
91 | parser.add_argument(
92 | '--resize_image_to',
93 | type=int,
94 | default=1024,
95 | help='Resize the largest image dimension to this value (default: 1024, '
96 | '0 does nothing).')
97 | parser.add_argument(
98 | '--model_version',
99 | type=str,
100 | default='v1',
101 | choices=["v0", "v1"])
102 | parser.add_argument(
103 | '--device',
104 | type=str,
105 | default='cpu',
106 | choices=["cpu", 'cuda', 'mps']
107 | )
108 | parser.add_argument(
109 | "--save_path",
110 | default=os.path.join('..', 'benchmark-features'),
111 | type=str,
112 | help='Path to store the features')
113 | parser.add_argument(
114 | "--method_name", default='lanet', type=str)
115 | parser.add_argument(
116 | "--dataset",
117 | default='all',
118 | type=str,
119 | choices=["all", "phototourism", "pragueparks"])
120 | parser.add_argument(
121 | "--norm_desc",
122 | default=False,
123 | type=str2bool,
124 | help='L2Norm of descriptors')
125 | opt, unparsed = parser.parse_known_args()
126 | print(opt)
127 | vv = opt.model_version
128 | device = torch.device(opt.device)
129 | if vv == 'v1':
130 | keypoint_net = PointModel_v1(is_test=True)
131 | checkpoint = torch.load('third_party/lanet/checkpoints/PointModel_v1.pth',
132 | map_location=device)
133 | else:
134 | keypoint_net = PointModel_v0(is_test=True)
135 | checkpoint = torch.load('third_party/lanet/checkpoints/PointModel_v0.pth',
136 | map_location=device)
137 |
138 | keypoint_net.load_state_dict(checkpoint['model_state'])
139 | keypoint_net.eval()
140 | keypoint_net=keypoint_net.to(device)
141 | INPUT_DIR = opt.datasets_folder
142 | modelname = f'{opt.method_name}_{opt.model_version}'
143 | if opt.norm_desc:
144 | modelname+='_norm'
145 | if opt.resize_image_to > 0:
146 | modelname+= f'_{opt.resize_image_to}'
147 | else:
148 | modelname+= f'_fullres'
149 | OUT_DIR = os.path.join(opt.save_path, modelname)
150 | os.makedirs(OUT_DIR, exist_ok=True)
151 | print (f"Will save to {OUT_DIR}")
152 | if opt.dataset == 'all':
153 | datasets = ['phototourism', 'pragueparks']#[x for x in os.listdir(INPUT_DIR) if (os.path.isdir(os.path.join(INPUT_DIR, x)))]
154 | else:
155 | datasets = [opt.dataset]
156 | for ds in datasets:
157 | ds_in_path = os.path.join(INPUT_DIR, ds)
158 | ds_out_path = os.path.join(OUT_DIR, ds)
159 | os.makedirs(ds_out_path, exist_ok=True)
160 | seqs = [x for x in os.listdir(ds_in_path) if os.path.isdir(os.path.join(ds_in_path, x))]
161 | for seq in seqs:
162 | print (seq)
163 | if os.path.isdir(os.path.join(ds_in_path, seq, 'set_100')):
164 | seq_in_path = os.path.join(ds_in_path, seq, 'set_100', 'images')
165 | else:
166 | seq_in_path = os.path.join(ds_in_path, seq)
167 | seq_out_path = os.path.join(ds_out_path, seq)
168 | os.makedirs(seq_out_path, exist_ok=True)
169 | img_fnames = os.listdir(seq_in_path)
170 | num_kp = []
171 | with h5py.File(f'{seq_out_path}/keypoints.h5', mode='w') as f_kp, \
172 | h5py.File(f'{seq_out_path}/descriptors.h5', mode='w') as f_desc, \
173 | h5py.File(f'{seq_out_path}/scores.h5', mode='w') as f_score, \
174 | h5py.File(f'{seq_out_path}/angles.h5', mode='w') as f_ang, \
175 | h5py.File(f'{seq_out_path}/scales.h5', mode='w') as f_scale:
176 | for img_fname in tqdm(img_fnames):
177 | img_fname_full = os.path.join(seq_in_path, img_fname)
178 | key = os.path.splitext(os.path.basename(img_fname))[0]
179 | kps, resps, descs = extract_features(img_fname_full, keypoint_net, device,
180 | opt.num_kp,
181 | opt.resize_image_to,
182 | opt.norm_desc)
183 | keypoints, scales, angles, responses = convert_imc(kps, resps)
184 | f_desc[key] = descs.reshape(-1, 256).detach().cpu().numpy()
185 | f_score[key] = responses
186 | f_ang[key] = angles
187 | f_kp[key] = keypoints
188 | f_scale[key] = scales
189 | num_kp.append(len(keypoints))
190 | print(f'Finished processing "{ds}/{seq}" -> {np.array(num_kp).mean()} features/image')
191 | print (f"Result is saved to {OUT_DIR}")
192 |
--------------------------------------------------------------------------------
/extract_lfnet.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import os
3 | import sys
4 | from PIL import Image
5 | import numpy as np
6 | import argparse
7 | import glob
8 | import h5py
9 | import json
10 | import tensorflow as tf
11 | import importlib
12 | import time
13 | import cv2
14 | from tqdm import tqdm
15 | import pickle
16 |
17 | sys.path.insert(0,os.path.join('third_party','lfnet'))
18 |
19 | from mydatasets import *
20 |
21 | from det_tools import *
22 | from eval_tools import draw_keypoints
23 | from common.tf_train_utils import get_optimizer
24 | from common.argparse_utils import *
25 | from imageio import imread, imsave
26 | from inference import *
27 | from run_lfnet import build_networks
28 |
29 | MODEL_PATH = './third_party/lfnet/models'
30 | if MODEL_PATH not in sys.path:
31 | sys.path.append(MODEL_PATH)
32 |
33 | # Adapted from third_party/r2d2/extract.py
34 | if __name__ == '__main__':
35 | parser = get_parser()
36 |
37 | general_arg = add_argument_group('General', parser)
38 | general_arg.add_argument('--num_threads', type=int, default=8,
39 | help='the number of threads (for dataset)')
40 | general_arg.add_argument(
41 | "--subset",
42 | default='both',
43 | type=str,
44 | help='Options: "val", "test", "both"')
45 |
46 | io_arg = add_argument_group('In/Out', parser)
47 | io_arg.add_argument('--in_dir', type=str, default=os.path.join('..', 'imw-2020'),
48 | help='input image directory')
49 | # io_arg.add_argument('--in_dir', type=str, default='./release/outdoor_examples/images/sacre_coeur/dense/images',
50 | # help='input image directory')
51 | io_arg.add_argument('--out_dir', type=str, required=True,
52 | help='where to save keypoints')
53 |
54 | model_arg = add_argument_group('Model', parser)
55 | model_arg.add_argument('--model', type=str, default='./third_party/lfnet/release/lfnet-norotaug/',
56 | help='model file or directory')
57 | model_arg.add_argument('--top_k', type=int, default=2000,
58 | help='number of keypoints')
59 | model_arg.add_argument('--max_longer_edge', type=int, default=0,
60 | help='resize image (do nothing if max_longer_edge <= 0)')
61 |
62 | tmp_config, unparsed = get_config(parser)
63 |
64 | if len(unparsed) > 0:
65 | raise ValueError('Miss finding argument: unparsed={}\n'.format(unparsed))
66 |
67 | # restore other hyperparams to build model
68 | if os.path.isdir(tmp_config.model):
69 | config_path = os.path.join(tmp_config.model, 'config.pkl')
70 | else:
71 | config_path = os.path.join(os.path.dirname(tmp_config.model), 'config.pkl')
72 | try:
73 | with open(config_path, 'rb') as f:
74 | config = pickle.load(f)
75 | except:
76 | raise ValueError('Fail to open {}'.format(config_path))
77 |
78 | for attr, dst_val in sorted(vars(tmp_config).items()):
79 | if hasattr(config, attr):
80 | src_val = getattr(config, attr)
81 | if src_val != dst_val:
82 | setattr(config, attr, dst_val)
83 | else:
84 | setattr(config, attr, dst_val)
85 |
86 | seqs = []
87 | if config.subset not in ['val', 'test', 'both']:
88 | raise ValueError('Unknown value for --subset')
89 | if config.subset in ['val', 'both']:
90 | with open(os.path.join('data', 'val.json')) as f:
91 | seqs += json.load(f)
92 | if config.subset in ['test', 'both']:
93 | with open(os.path.join('data', 'test.json')) as f:
94 | seqs += json.load(f)
95 |
96 |
97 | # Build Networks
98 | tf.reset_default_graph()
99 |
100 | photo_ph = tf.placeholder(tf.float32, [1, None, None, 1]) # input grayscale image, normalized by 0~1
101 | is_training = tf.constant(False) # Always False in testing
102 |
103 | ops = build_networks(config, photo_ph, is_training)
104 |
105 | tfconfig = tf.ConfigProto()
106 | tfconfig.gpu_options.allow_growth = True
107 | sess = tf.Session(config=tfconfig)
108 | sess.run(tf.global_variables_initializer())
109 |
110 | # load model
111 | saver = tf.train.Saver()
112 | print('Load trained models...')
113 |
114 | if os.path.isdir(config.model):
115 | checkpoint = tf.train.latest_checkpoint(config.model)
116 | model_dir = config.model
117 | else:
118 | checkpoint = config.model
119 | model_dir = os.path.dirname(config.model)
120 |
121 |
122 | if checkpoint is not None:
123 | print('Checkpoint', os.path.basename(checkpoint))
124 | print("[{}] Resuming...".format(time.asctime()))
125 | saver.restore(sess, checkpoint)
126 | else:
127 | raise ValueError('Cannot load model from {}'.format(model_dir))
128 | print('Done.')
129 |
130 |
131 | print('Processing the following scenes: {}'.format(seqs))
132 | for seq in seqs:
133 |
134 | print('Processing scene "{}"'.format(seq))
135 |
136 | if not os.path.isdir('{}/{}'.format(config.out_dir, seq)):
137 | os.makedirs('{}/{}'.format(config.out_dir, seq))
138 |
139 | images = glob.glob('{}/{}/*.jpg'.format(config.in_dir, seq))
140 |
141 | num_kp = []
142 | with h5py.File('{}/{}/keypoints.h5'.format(config.out_dir, seq), 'w') as f_kp, \
143 | h5py.File('{}/{}/descriptors.h5'.format(config.out_dir, seq), 'w') as f_desc:
144 | for fn in images:
145 | key = os.path.splitext(os.path.basename(fn))[0]
146 | print(key)
147 | photo = imread(fn)
148 | height, width = photo.shape[:2]
149 | longer_edge = max(height, width)
150 | if config.max_longer_edge > 0 and longer_edge > config.max_longer_edge:
151 | if height > width:
152 | new_height = config.max_longer_edge
153 | new_width = int(width * config.max_longer_edge / height)
154 | else:
155 | new_height = int(height * config.max_longer_edge / width)
156 | new_width = config.max_longer_edge
157 | photo = cv2.resize(photo, (new_width, new_height))
158 | height, width = photo.shape[:2]
159 | rgb = photo.copy()
160 | if photo.ndim == 3 and photo.shape[-1] == 3:
161 | photo = cv2.cvtColor(photo, cv2.COLOR_RGB2GRAY)
162 | photo = photo[None,...,None].astype(np.float32) / 255.0 # normalize 0-1
163 | assert photo.ndim == 4 # [1,H,W,1]
164 |
165 | feed_dict = {
166 | photo_ph: photo,
167 | }
168 |
169 | fetch_dict = {
170 | 'kpts': ops['kpts'],
171 | 'feats': ops['feats'],
172 | 'kpts_scale': ops['kpts_scale'],
173 | 'kpts_ori': ops['kpts_ori'],
174 | 'scale_maps': ops['scale_maps'],
175 | 'degree_maps': ops['degree_maps'],
176 | }
177 |
178 | outs = sess.run(fetch_dict, feed_dict=feed_dict)
179 |
180 |
181 | f_kp[key] = outs['kpts']
182 | f_desc[key] = outs['feats']
183 | num_kp.append(len(f_kp[key]))
184 |
185 | print('Image "{}/{}" -> {} features'.format(
186 | seq, key, num_kp[-1]))
187 |
188 | print('Finished processing scene "{}" -> {} features/image'.format(
189 | seq, np.array(num_kp).mean()))
190 |
--------------------------------------------------------------------------------
/extract_ml_superpoint.py:
--------------------------------------------------------------------------------
1 | import os
2 | import h5py
3 | from tqdm import tqdm
4 | import matplotlib.pyplot as plt
5 | import numpy as np
6 | import cv2
7 | import torch
8 | import torch.nn.functional as F
9 | import argparse
10 | import sys
11 | import yaml
12 | from copy import deepcopy
13 | torch.set_default_tensor_type(torch.FloatTensor)
14 |
15 | def str2bool(v):
16 | if v.lower() in ('yes', 'true', 't', 'y', '1'):
17 | return True
18 | elif v.lower() in ('no', 'false', 'f', 'n', '0'):
19 | return False
20 |
21 | def save_h5(dict_to_save, filename):
22 | """Saves dictionary to hdf5 file"""
23 |
24 | with h5py.File(filename, 'w') as f:
25 | for key in dict_to_save:
26 | f.create_dataset(key, data=dict_to_save[key])
27 |
28 | sys.path.insert(0, f'{os.getcwd()}/third_party/superpoint_forked')
29 | from superpoint import SuperPointFrontend
30 | import kornia as K
31 | # data loading
32 |
33 |
34 |
35 | def convert_imc(kps, resps):
36 | keypoints = kps.reshape(-1, 2)
37 | nkp = len(keypoints)
38 | scales = np.ones((nkp, 1)).astype(np.float32)
39 | angles = np.zeros((nkp, 1)).astype(np.float32)
40 | responses = resps.reshape(-1, 1)
41 | return keypoints, scales, angles, responses
42 |
43 |
44 | def extract_features(img_fname, keypoint_net, device, MAX_KP, max_size, norm_desc):
45 | img = cv2.cvtColor(cv2.imread(img_fname), cv2.COLOR_BGR2RGB)
46 | timg = K.image_to_tensor(img, False).float()/255.
47 | timg = timg.to(device)
48 | timg = K.color.rgb_to_grayscale(timg)
49 | H, W = timg.shape[2:]
50 | if max_size>0:
51 | if max_size % 16 != 0:
52 | max_size = int(max_size - (max_size % 16))
53 | min_size = int(min(H, W) * max_size / float(max(H, W)))
54 | if min_size % 16 !=0:
55 | min_size = int(min_size - (min_size % 16))
56 | if H > W:
57 | out_size = (max_size, min_size)
58 | else:
59 | out_size = (min_size, max_size)
60 | with torch.no_grad():
61 | timg_res = K.geometry.resize(timg, out_size)
62 | else:
63 | timg_res = timg
64 | with torch.no_grad():
65 | H2, W2 = timg_res.shape[2:]
66 | coef_h = (H/float(H2))
67 | coef_w = (W/float(W2))
68 | kp1, descs1, heatmap1 = superpoint.run(timg_res[0,0].detach().cpu().numpy())
69 | kp1, descs1, heatmap1 = torch.from_numpy(kp1), torch.from_numpy(descs1), torch.from_numpy(heatmap1)
70 | coord_1 = kp1.T
71 | score_1 = deepcopy(coord_1[:, 2])
72 | coord_1 = deepcopy(coord_1[:, :2])
73 | desc1 = descs1.T
74 | if norm_desc:
75 | desc1 = F.normalize(desc1, dim=1, p=2)
76 | score_1 = score_1.reshape(-1)
77 | sorted_sc, indices = torch.sort(score_1, descending=True)
78 | idxs = indices[:MAX_KP].numpy()
79 | resps = score_1[idxs].detach().cpu().numpy()
80 | kps = coord_1[idxs]
81 | kps[:, 0] *= coef_w
82 | kps[:, 1] *= coef_h
83 | descs = desc1[idxs]
84 | return kps.detach().cpu().numpy().reshape(-1, 2), resps, descs.detach().cpu().numpy()
85 |
86 | if __name__ == '__main__':
87 | parser = argparse.ArgumentParser()
88 | parser.add_argument(
89 | "--datasets_folder",
90 | default=os.path.join('..', 'imw-2020'),
91 | help="path to datasets folder",
92 | type=str)
93 | parser.add_argument(
94 | '--num_kp',
95 | type=int,
96 | default=2048,
97 | help='number of keypoints')
98 | parser.add_argument(
99 | '--resize_image_to',
100 | type=int,
101 | default=1024,
102 | help='Resize the largest image dimension to this value (default: 1024, '
103 | '0 does nothing).')
104 | parser.add_argument(
105 | '--device',
106 | type=str,
107 | default='cpu',
108 | choices=["cpu", 'cuda', 'mps']
109 | )
110 | parser.add_argument(
111 | "--save_path",
112 | default=os.path.join('..', 'benchmark-features'),
113 | type=str,
114 | help='Path to store the features')
115 | parser.add_argument(
116 | "--method_name", default='superpoint_magicleap', type=str)
117 | parser.add_argument(
118 | "--dataset",
119 | default='all',
120 | type=str,
121 | choices=["all", "phototourism", "pragueparks"])
122 | parser.add_argument(
123 | "--norm_desc",
124 | default=False,
125 | type=str2bool,
126 | help='L2Norm of descriptors')
127 | opt, unparsed = parser.parse_known_args()
128 | device = torch.device(opt.device)
129 | sp_weights_fname = 'third_party/superpoint_forked/superpoint_v1.pth'
130 | superpoint = SuperPointFrontend(sp_weights_fname, 4, 0.00015, 0.7, cuda=opt.device=='cuda')
131 | superpoint.net = superpoint.net.to(device)
132 |
133 | INPUT_DIR = opt.datasets_folder
134 | modelname = f'{opt.method_name}'
135 | if opt.norm_desc:
136 | modelname+='_norm'
137 | if opt.resize_image_to > 0:
138 | modelname+= f'_{opt.resize_image_to}'
139 | else:
140 | modelname+= f'_fullres'
141 | OUT_DIR = os.path.join(opt.save_path, modelname)
142 | os.makedirs(OUT_DIR, exist_ok=True)
143 | print (f"Will save to {OUT_DIR}")
144 | if opt.dataset == 'all':
145 | datasets = [x for x in os.listdir(INPUT_DIR) if (os.path.isdir(os.path.join(INPUT_DIR, x)))]
146 | else:
147 | datasets = [opt.dataset]
148 | for ds in datasets:
149 | ds_in_path = os.path.join(INPUT_DIR, ds)
150 | ds_out_path = os.path.join(OUT_DIR, ds)
151 | os.makedirs(ds_out_path, exist_ok=True)
152 | seqs = [x for x in os.listdir(ds_in_path) if os.path.isdir(os.path.join(ds_in_path, x))]
153 | for seq in seqs:
154 | print (seq)
155 | if os.path.isdir(os.path.join(ds_in_path, seq, 'set_100')):
156 | seq_in_path = os.path.join(ds_in_path, seq, 'set_100', 'images')
157 | else:
158 | seq_in_path = os.path.join(ds_in_path, seq)
159 | seq_out_path = os.path.join(ds_out_path, seq)
160 | os.makedirs(seq_out_path, exist_ok=True)
161 | img_fnames = os.listdir(seq_in_path)
162 | num_kp = []
163 | with h5py.File(f'{seq_out_path}/keypoints.h5', mode='w') as f_kp, \
164 | h5py.File(f'{seq_out_path}/descriptors.h5', mode='w') as f_desc, \
165 | h5py.File(f'{seq_out_path}/scores.h5', mode='w') as f_score, \
166 | h5py.File(f'{seq_out_path}/angles.h5', mode='w') as f_ang, \
167 | h5py.File(f'{seq_out_path}/scales.h5', mode='w') as f_scale:
168 | for img_fname in tqdm(img_fnames):
169 | img_fname_full = os.path.join(seq_in_path, img_fname)
170 | key = os.path.splitext(os.path.basename(img_fname))[0]
171 | kps, resps, descs = extract_features(img_fname_full, superpoint, device,
172 | opt.num_kp,
173 | opt.resize_image_to,
174 | opt.norm_desc)
175 | keypoints, scales, angles, responses = convert_imc(kps, resps)
176 | f_desc[key] = descs.reshape(-1, 256)
177 | f_score[key] = responses
178 | f_kp[key] = keypoints
179 | f_ang[key] = angles
180 | f_scale[key] = scales
181 | num_kp.append(len(keypoints))
182 | print(f'Finished processing "{ds}/{seq}" -> {np.array(num_kp).mean()} features/image')
183 | print (f"Result is saved to {OUT_DIR}")
184 |
185 |
--------------------------------------------------------------------------------
/extract_r2d2.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from PIL import Image
4 | import numpy as np
5 | import torch
6 | import argparse
7 | from glob import glob
8 | import h5py
9 | import json
10 |
11 | sys.path.append(os.path.join('third_party', 'r2d2'))
12 | from third_party.r2d2.tools import common
13 | from third_party.r2d2.tools.dataloader import norm_RGB
14 | from third_party.r2d2.nets.patchnet import *
15 | from third_party.r2d2.extract import load_network, NonMaxSuppression, extract_multiscale
16 |
17 | # Adapted from third_party/r2d2/extract.py
18 | if __name__ == '__main__':
19 | parser = argparse.ArgumentParser("Extract R2D2 features for IMW2020")
20 |
21 | parser.add_argument("--model", type=str, required=True, help='Model path')
22 | parser.add_argument(
23 | "--num_keypoints", type=int, default=5000, help='Number of keypoints')
24 | parser.add_argument("--scale-f", type=float, default=2**0.25)
25 | parser.add_argument("--min-size", type=int, default=256)
26 | parser.add_argument("--max-size", type=int, default=1024)
27 | parser.add_argument("--min-scale", type=float, default=0)
28 | parser.add_argument("--max-scale", type=float, default=1)
29 | parser.add_argument("--reliability-thr", type=float, default=0.7)
30 | parser.add_argument("--repeatability-thr", type=float, default=0.7)
31 | parser.add_argument(
32 | "--gpu", type=int, nargs='+', default=[0], help='Use -1 for CPU')
33 | parser.add_argument(
34 | "--data_path", type=str, default=os.path.join('..', 'imw-2020'))
35 | parser.add_argument(
36 | "--save_path",
37 | type=str,
38 | required=True,
39 | help='Path to store the features')
40 | parser.add_argument(
41 | "--subset",
42 | default='both',
43 | type=str,
44 | help='Options: "val", "test", "both"')
45 |
46 | args = parser.parse_args()
47 |
48 | seqs = []
49 | if args.subset not in ['val', 'test', 'both']:
50 | raise ValueError('Unknown value for --subset')
51 | if args.subset in ['val', 'both']:
52 | with open(os.path.join('data', 'val.json')) as f:
53 | seqs += json.load(f)
54 | if args.subset in ['test', 'both']:
55 | with open(os.path.join('data', 'test.json')) as f:
56 | seqs += json.load(f)
57 | print('Processing the following scenes: {}'.format(seqs))
58 |
59 | iscuda = common.torch_set_gpu(args.gpu)
60 |
61 | net = load_network(args.model)
62 | if iscuda:
63 | net = net.cuda()
64 |
65 | detector = NonMaxSuppression(
66 | rel_thr=args.reliability_thr, rep_thr=args.repeatability_thr)
67 |
68 | for seq in seqs:
69 | print('Processing scene "{}"'.format(seq))
70 | if not os.path.isdir('{}/{}'.format(args.save_path, seq)):
71 | os.makedirs('{}/{}'.format(args.save_path, seq))
72 |
73 | images = glob('{}/{}/*.jpg'.format(args.data_path, seq))
74 |
75 | num_kp = []
76 | with h5py.File('{}/{}/keypoints.h5'.format(args.save_path, seq), 'w') as f_kp, \
77 | h5py.File('{}/{}/descriptors.h5'.format(args.save_path, seq), 'w') as f_desc, \
78 | h5py.File('{}/{}/scores.h5'.format(args.save_path, seq), 'w') as f_score, \
79 | h5py.File('{}/{}/scales.h5'.format(args.save_path, seq), 'w') as f_scale:
80 | for fn in images:
81 | key = os.path.splitext(os.path.basename(fn))[0]
82 | img = Image.open(fn).convert('RGB')
83 | img = norm_RGB(img)[None]
84 | if iscuda:
85 | img = img.cuda()
86 |
87 | xys, desc, scores = extract_multiscale(
88 | net,
89 | img,
90 | detector,
91 | scale_f=args.scale_f,
92 | min_scale=args.min_scale,
93 | max_scale=args.max_scale,
94 | min_size=args.min_size,
95 | max_size=args.max_size,
96 | verbose=False)
97 |
98 | kp = xys.cpu().numpy()[:, :2]
99 | scales = xys.cpu().numpy()[:, 2]
100 | desc = desc.cpu().numpy()
101 | scores = scores.cpu().numpy()
102 | idxs = scores.argsort()[-args.num_keypoints:]
103 |
104 | f_kp[key] = kp[idxs]
105 | f_desc[key] = desc[idxs]
106 | f_score[key] = scores[idxs]
107 | f_scale[key] = scales[idxs]
108 | num_kp.append(len(f_kp[key]))
109 |
110 | print('Image "{}/{}" -> {} features'.format(
111 | seq, key, num_kp[-1]))
112 |
113 | print('Finished processing scene "{}" -> {} features/image'.format(
114 | seq, np.array(num_kp).mean()))
115 |
--------------------------------------------------------------------------------
/extract_sift_kornia_affnet_desc.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from PIL import Image
4 | import numpy as np
5 | import torch.nn as nn
6 | import torch
7 | import argparse
8 | from glob import glob
9 | import h5py
10 | import json
11 | from torch import tensor
12 | sys.path.append(os.path.join('third_party', 'r2d2'))
13 | import kornia as K
14 | import kornia.feature as KF
15 | from kornia_moons.feature import *
16 | import cv2
17 | def get_local_descriptors(img, cv2_sift_kpts, kornia_descriptor, aff):
18 | #We will not train anything, so let's save time and memory by no_grad()
19 | with torch.no_grad():
20 | timg = K.color.rgb_to_grayscale(K.image_to_tensor(img, False))/255.
21 | timg = timg.cuda()
22 | lafs = laf_from_opencv_SIFT_kpts(cv2_sift_kpts).cuda()
23 | angles = KF.laf.get_laf_orientation(lafs)
24 | # We will estimate affine shape of the feature and re-orient the keypoints with the OriNet
25 | lafs_new = aff(lafs,timg)
26 | patches = KF.extract_patches_from_pyramid(timg,lafs_new, 32)
27 | B, N, CH, H, W = patches.size()
28 | # Descriptor accepts standard tensor [B, CH, H, W], while patches are [B, N, CH, H, W] shape
29 | # So we need to reshape a bit :)
30 | descs = kornia_descriptor(patches.view(B * N, CH, H, W)).view(B * N, -1)
31 | return descs.detach().cpu().numpy()
32 |
33 | if __name__ == '__main__':
34 | parser = argparse.ArgumentParser("Extract Kornia handcrafted features for IMW2020")
35 |
36 | parser.add_argument(
37 | "--num_keypoints", type=int, default=8000, help='Number of keypoints')
38 | parser.add_argument("--mrsize", type=float, default=6.0)
39 | parser.add_argument("--patchsize", type=float, default=32)
40 | parser.add_argument("--upright", type=bool, default=True)
41 | parser.add_argument("--affine", type=bool, default=True)
42 | parser.add_argument("--descriptor", type=str, default='HardNet', help='hardnet, sift, rootsift, tfeat, sosnet')
43 | parser.add_argument(
44 | "--data_path", type=str, default=os.path.join('..', 'data'))
45 | parser.add_argument(
46 | "--save_path",
47 | type=str,
48 | required=True,
49 | help='Path to store the features')
50 | parser.add_argument(
51 | "--subset",
52 | default='both',
53 | type=str,
54 | help='Options: "val", "test", "both"')
55 |
56 | args = parser.parse_args()
57 |
58 | seqs = []
59 | if args.subset not in ['val', 'test', 'both']:
60 | raise ValueError('Unknown value for --subset')
61 | if args.subset in ['val', 'both']:
62 | with open(os.path.join('data', 'val.json')) as f:
63 | seqs += json.load(f)
64 | if args.subset in ['test', 'both']:
65 | with open(os.path.join('data', 'test.json')) as f:
66 | seqs += json.load(f)
67 | print('Processing the following scenes: {}'.format(seqs))
68 | PS = args.patchsize
69 | device = torch.device('cpu')
70 | try:
71 | if torch.cuda.is_available():
72 | device = torch.device('cuda')
73 | except:
74 | print ('CPU mode')
75 | sift = cv2.SIFT_create(
76 | contrastThreshold=-10000, edgeThreshold=-10000)
77 | if args.descriptor.lower() == 'sift':
78 | descriptor = KF.SIFTDescriptor(PS, rootsift=False)
79 | elif args.descriptor.lower() == 'rootsift':
80 | descriptor = KF.SIFTDescriptor(PS, rootsift=True)
81 | elif args.descriptor.lower() == 'hardnet':
82 | PS = 32
83 | descriptor = KF.HardNet(True)
84 | elif args.descriptor.lower() == 'sosnet':
85 | PS = 32
86 | descriptor = KF.SOSNet(True)
87 | elif args.descriptor.lower() == 'tfeat':
88 | PS = 32
89 | descriptor = KF.TFeat(True)
90 | else:
91 | raise ValueError('Unknown descriptor')
92 | descriptor = descriptor.to(device)
93 | print (device)
94 | descriptor.eval()
95 | aff_est = KF.LAFAffNetShapeEstimator(True).to(device)
96 | aff_est.eval()
97 | from tqdm import tqdm
98 | def get_SIFT_keypoints(sift, img, lower_detection_th=False):
99 | # convert to gray-scale and compute SIFT keypoints
100 | keypoints = sift.detect(img, None)
101 | response = np.array([kp.response for kp in keypoints])
102 | respSort = np.argsort(response)[::-1]
103 | pt = np.array([kp.pt for kp in keypoints])[respSort]
104 | size = np.array([kp.size for kp in keypoints])[respSort]
105 | angle = np.array([kp.angle for kp in keypoints])[respSort]
106 | response = np.array([kp.response for kp in keypoints])[respSort]
107 | return pt, size, angle, response
108 | NUM_KP = args.num_keypoints
109 | for seq in seqs:
110 | print('Processing scene "{}"'.format(seq))
111 | if not os.path.isdir('{}/{}'.format(args.save_path, seq)):
112 | os.makedirs('{}/{}'.format(args.save_path, seq))
113 | images = glob('{}/{}/set_100/images/*.jpg'.format(args.data_path, seq))
114 | num_kp = []
115 | with h5py.File('{}/{}/keypoints.h5'.format(args.save_path, seq), 'w') as f_kp, \
116 | h5py.File('{}/{}/descriptors.h5'.format(args.save_path, seq), 'w') as f_desc, \
117 | h5py.File('{}/{}/scores.h5'.format(args.save_path, seq), 'w') as f_score, \
118 | h5py.File('{}/{}/angles.h5'.format(args.save_path, seq), 'w') as f_ang, \
119 | h5py.File('{}/{}/scales.h5'.format(args.save_path, seq), 'w') as f_scale:
120 | for fn in tqdm(images):
121 | key = os.path.splitext(os.path.basename(fn))[0]
122 | im = cv2.cvtColor(cv2.imread(fn), cv2.COLOR_BGR2RGB)
123 | pts, size, angle, response = get_SIFT_keypoints(sift, im)
124 | if args.upright:
125 | kpts = [
126 | cv2.KeyPoint(
127 | x=pt[0],
128 | y=pt[1],
129 | _size=size[i],
130 | _angle=0, _response=response[i]) for i, pt in enumerate(pts) if (pt not in pts[:i]) ]
131 | kpts = kpts[:NUM_KP]
132 | else:
133 | kpts = [
134 | cv2.KeyPoint(
135 | x=pt[0],
136 | y=pt[1],
137 | _size=size[i],
138 | _angle=angle[i], _response=response[i]) for i, pt in enumerate(pts) ]
139 | kpts = kpts[:NUM_KP]
140 | with torch.no_grad():
141 | descs = get_local_descriptors(im, kpts, descriptor, aff_est)
142 | keypoints = np.array([(x.pt[0], x.pt[1]) for x in kpts ]).reshape(-1, 2)
143 | scales = np.array([12.0* x.size for x in kpts ]).reshape(-1, 1)
144 | angles = np.array([x.angle for x in kpts ]).reshape(-1, 1)
145 | responses = np.array([x.response for x in kpts ]).reshape(-1, 1)
146 | f_kp[key] = keypoints
147 | f_desc[key] = descs
148 | f_score[key] = responses
149 | f_ang[key] = angles
150 | f_scale[key] = scales
151 | num_kp.append(len(keypoints))
152 | print('Finished processing scene "{}" -> {} features/image'.format(
153 | seq, np.array(num_kp).mean()))
154 |
--------------------------------------------------------------------------------
/extract_superoint_independent.py:
--------------------------------------------------------------------------------
1 | import os
2 | import h5py
3 | from tqdm import tqdm
4 | import matplotlib.pyplot as plt
5 | import numpy as np
6 | import cv2
7 | import torch
8 | import torch.nn.functional as F
9 | import argparse
10 | import sys
11 | import yaml
12 | from copy import deepcopy
13 | torch.set_default_tensor_type(torch.FloatTensor)
14 |
15 | def str2bool(v):
16 | if v.lower() in ('yes', 'true', 't', 'y', '1'):
17 | return True
18 | elif v.lower() in ('no', 'false', 'f', 'n', '0'):
19 | return False
20 |
21 | def save_h5(dict_to_save, filename):
22 | """Saves dictionary to hdf5 file"""
23 |
24 | with h5py.File(filename, 'w') as f:
25 | for key in dict_to_save:
26 | f.create_dataset(key, data=dict_to_save[key])
27 |
28 | sys.path.insert(0, f'{os.getcwd()}/third_party/pytorch-superpoint')
29 |
30 | import kornia as K
31 | # data loading
32 | from utils.loader import dataLoader_test as dataLoader
33 | from Val_model_heatmap import Val_model_heatmap as model_wrapper
34 |
35 |
36 | def convert_imc(kps, resps):
37 | keypoints = kps.reshape(-1, 2)
38 | nkp = len(keypoints)
39 | scales = np.ones((nkp, 1)).astype(np.float32)
40 | angles = np.zeros((nkp, 1)).astype(np.float32)
41 | responses = resps.reshape(-1, 1)
42 | return keypoints, scales, angles, responses
43 |
44 |
45 | def extract_features(img_fname, keypoint_net, device, MAX_KP, max_size, norm_desc, subpixel=False):
46 | img = cv2.cvtColor(cv2.imread(img_fname), cv2.COLOR_BGR2RGB)
47 | timg = K.image_to_tensor(img, False).float()/255.
48 | timg = timg.to(device)
49 | #timg_gray = K.color.rgb_to_grayscale(timg)
50 | H, W = timg.shape[2:]
51 | if max_size>0:
52 | if max_size % 16 != 0:
53 | max_size = int(max_size - (max_size % 16))
54 | min_size = int(min(H, W) * max_size / float(max(H, W)))
55 | if min_size % 16 !=0:
56 | min_size = int(min_size - (min_size % 16))
57 | if H > W:
58 | out_size = (max_size, min_size)
59 | else:
60 | out_size = (min_size, max_size)
61 | with torch.no_grad():
62 | timg_res = K.geometry.resize(timg, out_size)
63 | else:
64 | timg_res = timg
65 | with torch.no_grad():
66 | H2, W2 = timg_res.shape[2:]
67 | coef_h = (H/float(H2))
68 | coef_w = (W/float(W2))
69 | heatmap_batch = keypoint_net.run(K.color.rgb_to_grayscale(timg_res)) # heatmap: numpy [batch, 1, H, W]
70 | # heatmap to pts
71 | pts = val_agent.heatmap_to_pts()
72 | if subpixel:
73 | pts_subpixel = val_agent.soft_argmax_points(pts)
74 | pts = pts_subpixel[0]
75 | else:
76 | pts = pts[0]
77 | # heatmap, pts to desc
78 |
79 | coord_1 = pts.T
80 | score_1 = deepcopy(coord_1[:, 2])
81 | coord_1 = deepcopy(coord_1[:, :2])
82 | desc1 = val_agent.desc_to_sparseDesc()[0].T
83 | if norm_desc:
84 | desc1 = F.normalize(torch.from_numpy(desc1), dim=1, p=2).numpy()
85 | score_1 = score_1.reshape(-1)
86 | sorted_sc, indices = torch.sort(torch.from_numpy(score_1), descending=True)
87 | idxs = indices[:MAX_KP].numpy()
88 | resps = score_1[idxs]
89 | kps = coord_1[idxs]
90 | kps[:, 0] *= coef_w
91 | kps[:, 1] *= coef_h
92 | descs = desc1[idxs]
93 | return kps.reshape(-1, 2), resps, descs
94 |
95 | if __name__ == '__main__':
96 | parser = argparse.ArgumentParser()
97 | parser.add_argument(
98 | "--datasets_folder",
99 | default=os.path.join('..', 'imw-2020'),
100 | help="path to datasets folder",
101 | type=str)
102 | parser.add_argument(
103 | '--num_kp',
104 | type=int,
105 | default=2048,
106 | help='number of keypoints')
107 | parser.add_argument(
108 | '--resize_image_to',
109 | type=int,
110 | default=1024,
111 | help='Resize the largest image dimension to this value (default: 1024, '
112 | '0 does nothing).')
113 | parser.add_argument(
114 | '--trainset',
115 | type=str,
116 | default='coco',
117 | choices=["coco", "kitty"])
118 | parser.add_argument(
119 | '--subpix',
120 | type=str2bool,
121 | default=False)
122 | parser.add_argument(
123 | '--device',
124 | type=str,
125 | default='cpu',
126 | choices=["cpu", 'cuda', 'mps']
127 | )
128 | parser.add_argument(
129 | "--save_path",
130 | default=os.path.join('..', 'benchmark-features'),
131 | type=str,
132 | help='Path to store the features')
133 | parser.add_argument(
134 | "--method_name", default='superpoint_indep', type=str)
135 | parser.add_argument(
136 | "--dataset",
137 | default='all',
138 | type=str,
139 | choices=["all", "phototourism", "pragueparks"])
140 | parser.add_argument(
141 | "--norm_desc",
142 | default=False,
143 | type=str2bool,
144 | help='L2Norm of descriptors')
145 | opt, unparsed = parser.parse_known_args()
146 | device = torch.device(opt.device)
147 | print(opt)
148 | if opt.trainset == 'coco':
149 | conf_filename = 'third_party/pytorch-superpoint/logs/superpoint_coco_heat2_0/config.yml'
150 | weights_fname = 'third_party/pytorch-superpoint/logs/superpoint_coco_heat2_0/checkpoints/superPointNet_170000_checkpoint.pth.tar'
151 | elif opt.trainset == 'kitty':
152 |
153 | weights_fname = 'third_party/pytorch-superpoint/logs/superpoint_kitti_heat2_0/checkpoints/superPointNet_50000_checkpoint.pth.tar'
154 | conf_filename = 'third_party/pytorch-superpoint/logs/superpoint_kitti_heat2_0/config.yml'
155 | else:
156 | pass
157 | with open(conf_filename, 'r') as f:
158 | config = yaml.load(f)
159 | config['model']['pretrained']=weights_fname
160 | config['model']['nn_thresh'] = 1.0
161 | config['model']['detection_threshold'] = 0.001
162 | # load frontend
163 | val_agent = model_wrapper(config['model'], device=device)
164 | val_agent.loadModel()
165 | val_agent.net.eval()
166 | val_agent.net=val_agent.net.to(device)
167 |
168 | INPUT_DIR = opt.datasets_folder
169 | modelname = f'{opt.method_name}_{opt.trainset}'
170 | if opt.norm_desc:
171 | modelname+='_norm'
172 | if opt.resize_image_to > 0:
173 | modelname+= f'_{opt.resize_image_to}'
174 | else:
175 | modelname+= f'_fullres'
176 | if opt.subpix:
177 | modelname+='_subpix'
178 | OUT_DIR = os.path.join(opt.save_path, modelname)
179 | os.makedirs(OUT_DIR, exist_ok=True)
180 | print (f"Will save to {OUT_DIR}")
181 | if opt.dataset == 'all':
182 | datasets = ['phototourism', 'pragueparks']#[x for x in os.listdir(INPUT_DIR) if (os.path.isdir(os.path.join(INPUT_DIR, x)))]
183 | else:
184 | datasets = [opt.dataset]
185 | for ds in datasets:
186 | ds_in_path = os.path.join(INPUT_DIR, ds)
187 | ds_out_path = os.path.join(OUT_DIR, ds)
188 | os.makedirs(ds_out_path, exist_ok=True)
189 | seqs = [x for x in os.listdir(ds_in_path) if os.path.isdir(os.path.join(ds_in_path, x))]
190 | for seq in seqs:
191 | print (seq)
192 | if os.path.isdir(os.path.join(ds_in_path, seq, 'set_100')):
193 | seq_in_path = os.path.join(ds_in_path, seq, 'set_100', 'images')
194 | else:
195 | seq_in_path = os.path.join(ds_in_path, seq)
196 | seq_out_path = os.path.join(ds_out_path, seq)
197 | os.makedirs(seq_out_path, exist_ok=True)
198 | img_fnames = os.listdir(seq_in_path)
199 | num_kp = []
200 | with h5py.File(f'{seq_out_path}/keypoints.h5', mode='w') as f_kp, \
201 | h5py.File(f'{seq_out_path}/descriptors.h5', mode='w') as f_desc, \
202 | h5py.File(f'{seq_out_path}/scores.h5', mode='w') as f_score, \
203 | h5py.File(f'{seq_out_path}/angles.h5', mode='w') as f_ang, \
204 | h5py.File(f'{seq_out_path}/scales.h5', mode='w') as f_scale:
205 | for img_fname in tqdm(img_fnames):
206 | img_fname_full = os.path.join(seq_in_path, img_fname)
207 | key = os.path.splitext(os.path.basename(img_fname))[0]
208 | kps, resps, descs = extract_features(img_fname_full, val_agent, device,
209 | opt.num_kp,
210 | opt.resize_image_to,
211 | opt.norm_desc,
212 | opt.subpix)
213 | keypoints, scales, angles, responses = convert_imc(kps, resps)
214 | f_desc[key] = descs.reshape(-1, 256)
215 | f_score[key] = responses
216 | f_ang[key] = angles
217 | f_scale[key] = scales
218 | f_kp[key] = keypoints
219 | num_kp.append(len(keypoints))
220 | print(f'Finished processing "{ds}/{seq}" -> {np.array(num_kp).mean()} features/image')
221 | print (f"Result is saved to {OUT_DIR}")
222 |
--------------------------------------------------------------------------------
/generate_image_lists.py:
--------------------------------------------------------------------------------
1 | import os
2 | from glob import glob
3 |
4 | src = os.path.join('..', 'imw-2020')
5 |
6 | seqs = [os.path.basename(p) for p in glob(os.path.join(src, '*'))]
7 | print(seqs)
8 |
9 | if not os.path.isdir('txt'):
10 | os.makedirs('txt')
11 |
12 | for seq in seqs:
13 | ims = glob(os.path.join(src, seq, '*.jpg'))
14 | with open(os.path.join('txt', 'list-{}.txt'.format(seq)), 'w') as f:
15 | for im in ims:
16 | f.write('{}\n'.format(im))
17 |
--------------------------------------------------------------------------------
/generate_yaml.py:
--------------------------------------------------------------------------------
1 |
2 | import argparse
3 | import yaml
4 | import os
5 | if not os.path.isdir('yaml'):
6 | os.makedirs('yaml')
7 |
8 | model_dict = {}
9 | model_dict['contextdesc++'] = 'contextdesc++/model.ckpt-400000'
10 | model_dict['reg_model'] = 'retrieval_model/model.ckpt-550000'
11 | model_dict['contextdesc++_upright'] ='contextdesc++_upright/model.ckpt-390000'
12 |
13 | parser = argparse.ArgumentParser(description='Geenerate yaml for contextdesc script')
14 |
15 | parser.add_argument(
16 | '--data_root',
17 | default = '../imw-2020',
18 | type = str,
19 | help = 'path to dataset folder')
20 |
21 | parser.add_argument(
22 | '--dump_root',
23 | default = '../benchmark-features',
24 | type = str,
25 | help = 'path to dump folder')
26 |
27 | parser.add_argument(
28 | '--num_keypoints',
29 | default = 8000,
30 | type = int,
31 | help = 'number of keypoints to extract'
32 | )
33 |
34 | parser.add_argument(
35 | '--upright',
36 | action='store_true',
37 | default=False,
38 | help = 'number of keypoints to extract'
39 | )
40 |
41 | args, unparsed = parser.parse_known_args()
42 |
43 | dict_file = {}
44 | dict_file['data_name']='imw2019'
45 | dict_file['data_split'] =''
46 | dict_file['data_root'] = args.data_root
47 | dict_file['all_jpeg'] = True
48 | dict_file['truncate'] = [0, None]
49 |
50 | dict_file['pretrained'] = {}
51 | dict_file['pretrained']['reg_model'] = 'third_party/contextdesc/pretrained/' + model_dict['reg_model']
52 |
53 | if args.upright:
54 | dict_file['pretrained']['loc_model'] = 'third_party/contextdesc/pretrained/' + model_dict['contextdesc++_upright']
55 | else:
56 | dict_file['pretrained']['loc_model'] = 'third_party/contextdesc/pretrained/' + model_dict['contextdesc++']
57 |
58 | dict_file['reg_feat'] ={}
59 | dict_file['reg_feat']['infer'] = True
60 | dict_file['reg_feat']['overwrite']= False
61 | dict_file['reg_feat']['max_dim']= 1024
62 |
63 | dict_file['loc_feat'] = {}
64 | dict_file['loc_feat']['infer']= True
65 | dict_file['loc_feat']['overwrite']= False
66 | dict_file['loc_feat']['n_feature']= args.num_keypoints
67 | dict_file['loc_feat']['batch_size']= 512
68 | dict_file['loc_feat']['dense_desc']= False
69 | dict_file['loc_feat']['peak_thld']= -10000
70 | dict_file['loc_feat']['edge_thld']= -10000
71 | dict_file['loc_feat']['max_dim']= 1280
72 | dict_file['loc_feat']['upright']= args.upright
73 | dict_file['loc_feat']['scale_diff']= True
74 |
75 | dict_file['aug_feat'] = {}
76 | dict_file['aug_feat']['infer']= True
77 | dict_file['aug_feat']['overwrite']= False
78 | dict_file['aug_feat']['reg_feat_dim']= 2048
79 | dict_file['aug_feat']['quantz']= False
80 |
81 | dict_file['post_format'] = {}
82 | dict_file['post_format']['enable'] = True
83 | dict_file['post_format']['suffix'] = ''
84 |
85 | dict_file['dump_root'] = os.path.join(args.dump_root,'tmp_contextdesc')
86 | dict_file['submission_root'] = os.path.join(args.dump_root,'contextdesc_{}'.format(dict_file['loc_feat']['n_feature']))
87 |
88 | with open(r'yaml/imw-2020.yaml', 'w') as file:
89 | documents = yaml.dump(dict_file, file)
--------------------------------------------------------------------------------
/misc/colmap/extract_colmap_feats_to_db.py:
--------------------------------------------------------------------------------
1 | import os
2 | import argparse
3 |
4 | def extract_features(dsp, upright_data_path, save_path):
5 |
6 | if not os.path.exists(save_path):
7 | os.makedirs(save_path)
8 |
9 | for folder in os.listdir(upright_data_path):
10 |
11 | images_path = os.path.join(upright_data_path, folder, 'set_100', 'images')
12 | database_path = os.path.join(save_path, folder+'.db')
13 | print('colmap database_creator --database_path {}'.format(database_path))
14 |
15 | os.system('colmap database_creator --database_path {}'.format(database_path))
16 |
17 | if dsp:
18 | os.system(
19 | 'CUDA_VISIBLE_DEVICES="0" colmap feature_extractor --database_path {} --image_path {} --SiftExtraction.gpu_index 0'
20 | ' --SiftExtraction.estimate_affine_shape 1 --SiftExtraction.edge_threshold 10000 --SiftExtraction.peak_threshold 0.00000001 --SiftExtraction.domain_size_pooling 1'.format(database_path,
21 | images_path))
22 | else:
23 | os.system('CUDA_VISIBLE_DEVICES="0" colmap feature_extractor --database_path {} --image_path {}'
24 | ' --SiftExtraction.estimate_affine_shape 1 --SiftExtraction.gpu_index 0 --SiftExtraction.edge_threshold 10000 --SiftExtraction.peak_threshold 0.00000001'.format(database_path, images_path))
25 |
26 | if __name__ == '__main__':
27 |
28 | parser = argparse.ArgumentParser()
29 |
30 | parser.add_argument(
31 | "--upright_data_path",
32 | default=os.path.join('../../..', 'data_upright'),
33 | type=str,
34 | help='Path to the dataset')
35 |
36 | parser.add_argument(
37 | "--save_path",
38 | default=os.path.join('../../..', 'colmap_descriptors'),
39 | type=str,
40 | help='Path to store the features')
41 |
42 | args = parser.parse_args()
43 |
44 | if not os.path.exists(args.save_path):
45 | os.makedirs(args.save_path)
46 |
47 | extract_features(False, args.upright_data_path, args.save_path)
48 | extract_features(True, args.upright_data_path, args.save_path.replace('colmap_descriptors','colmap_descriptors_dsp'))
--------------------------------------------------------------------------------
/misc/l2net/convert_l2net_weights_matconv_pytorch.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import scipy.io as sio
3 | import torch
4 | import torch.nn.init
5 | from misc.l2net.l2net_model import L2Net
6 |
7 | eps = 1e-10
8 |
9 | def check_ported(l2net_model, test_patch, img_mean):
10 |
11 | test_patch = test_patch.transpose(3, 2, 0, 1)-img_mean
12 | desc = l2net_model(torch.from_numpy(test_patch))
13 | print(desc)
14 | return desc
15 |
16 | if __name__ == '__main__':
17 |
18 | path_to_l2net_weights = 'descriptors/sfm-evaluation-benchmarking/third_party/l2net/matlab/L2Net-LIB+.mat'
19 |
20 | l2net_weights = sio.loadmat(path_to_l2net_weights)
21 |
22 | l2net_model = L2Net()
23 | l2net_model.eval()
24 |
25 | new_state_dict = l2net_model.state_dict().copy()
26 |
27 | conv_layers, bn_layers = {}, {}
28 | all_layer_weights = l2net_weights['net']['layers'][0][0][0]
29 | img_mean = l2net_weights['pixMean']
30 | conv_layers_to_track, bn_layers_to_track = [0,3,6,9,12,15,18], \
31 | [1,4,7,10,13,16,19]
32 | conv_i, bn_i = 0,0
33 |
34 | for layer in all_layer_weights:
35 |
36 | if 'weights' not in layer.dtype.names:
37 | continue
38 | layer_name = layer[0][0][0][0]
39 | layer_value = layer['weights'][0][0][0]
40 | if layer_name == 'conv':
41 | conv_layers[conv_layers_to_track[conv_i]] = layer_value
42 | conv_i+=1
43 | elif layer_name == 'bnormPair':
44 | bn_layers[bn_layers_to_track[bn_i]] = layer_value
45 | bn_i+=1
46 |
47 | for key, value in new_state_dict.items():
48 | layer_number = int(key.split('.')[1])
49 | if layer_number in conv_layers.keys():
50 | if 'weight' in key:
51 | new_state_dict[key] = torch.from_numpy(conv_layers[layer_number][0].transpose((3,2,0,1)))
52 | elif 'bias' in key:
53 | new_state_dict[key] = torch.from_numpy(conv_layers[layer_number][1]).squeeze()
54 | elif layer_number in bn_layers.keys():
55 | if 'running_mean' in key:
56 | new_state_dict[key] = torch.from_numpy(np.array([x[0] for x in bn_layers[layer_number][2]])).squeeze()
57 | elif 'running_var' in key:
58 | new_state_dict[key] = torch.from_numpy(np.array([x[1] for x in bn_layers[layer_number][2]] )** 2 -eps).squeeze()
59 | elif 'weight' in key:
60 | new_state_dict[key] = torch.from_numpy(np.ones(value.size()[0])).squeeze()
61 |
62 | else:
63 | continue
64 |
65 | l2net_model.load_state_dict(new_state_dict)
66 | l2net_model.eval()
67 |
68 | torch.save(l2net_model.state_dict(),'l2net_ported_weights_lib+.pth')
69 |
70 | # compare desc on test patch with matlab implementation
71 | # test_patch_batch = sio.loadmat('test_batch_img.mat')['testPatch']
72 | # check_ported(l2net_model, test_patch_batch, img_mean)
73 | #
74 | # test_patch_one = sio.loadmat('test_one.mat')['testPatch']
75 | # check_ported(l2net_model, np.expand_dims(np.expand_dims(test_patch_one, axis=2),axis=2), img_mean)
76 |
--------------------------------------------------------------------------------
/misc/l2net/l2net_model.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn.init
3 | import torch.nn as nn
4 |
5 | eps = 1e-10
6 |
7 | class L2Norm(nn.Module):
8 |
9 | def __init__(self):
10 | super(L2Norm,self).__init__()
11 | self.eps = 1e-10
12 |
13 | def forward(self, x):
14 | norm = torch.sqrt(torch.sum(x * x, dim = 1) + self.eps)
15 | x= x / norm.unsqueeze(-1).expand_as(x)
16 | return x
17 |
18 | class L2Net(nn.Module):
19 |
20 | def __init__(self):
21 | super(L2Net, self).__init__()
22 | self.features = nn.Sequential(
23 | nn.Conv2d(1, 32, kernel_size=3, padding=1, bias=True),
24 | nn.BatchNorm2d(32, affine=True, eps=eps),
25 | nn.ReLU(),
26 | nn.Conv2d(32, 32, kernel_size=3, padding=1, bias=True),
27 | nn.BatchNorm2d(32, affine=True, eps=eps),
28 | nn.ReLU(),
29 | nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1, bias=True),
30 | nn.BatchNorm2d(64, affine=True, eps=eps),
31 | nn.ReLU(),
32 | nn.Conv2d(64, 64, kernel_size=3, padding=1, bias=True),
33 | nn.BatchNorm2d(64, affine=True, eps=eps),
34 | nn.ReLU(),
35 | nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1, bias=True),
36 | nn.BatchNorm2d(128, affine=True, eps=eps),
37 | nn.ReLU(),
38 | nn.Conv2d(128, 128, kernel_size=3, padding=1, bias=True),
39 | nn.BatchNorm2d(128, affine=True, eps=eps),
40 | nn.ReLU(),
41 | nn.Conv2d(128, 128, kernel_size=8, bias=True),
42 | nn.BatchNorm2d(128, affine=True, eps=eps),
43 | )
44 | return
45 |
46 | def input_norm(self, x):
47 | # matlab norm
48 | z = x.contiguous().transpose(2, 3).contiguous().view(x.size(0),-1)
49 | x_minus_mean = z.transpose(0,1)-z.mean(1)
50 | sp = torch.std(z,1).detach()
51 | norm_inp = x_minus_mean/(sp+1e-12)
52 | norm_inp = norm_inp.transpose(0, 1).view(-1, 1, x.size(2), x.size(3)).transpose(2,3)
53 | return norm_inp
54 |
55 | def forward(self, input):
56 | norm_img = self.input_norm(input)
57 | x_features = self.features(norm_img)
58 | return nn.LocalResponseNorm(256,1*256,0.5,0.5)(x_features).view(input.size(0),-1)
59 |
--------------------------------------------------------------------------------
/misc/l2net/l2net_ported_weights_lib+.pth:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubc-vision/image-matching-benchmark-baselines/35dcd21c889583a22a4f612389a6b0d7c5af506b/misc/l2net/l2net_ported_weights_lib+.pth
--------------------------------------------------------------------------------
/run_d2net.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # D2-net
4 | echo "Extracting D2-net"
5 | python extract_d2net.py --method_name=d2net-singlescale_8000 --num_kp=8000
6 | python extract_d2net.py --multiscale --method_name=d2net-multiscale_8000 --num_kp=8000
7 |
--------------------------------------------------------------------------------
/run_delf.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 |
4 | seqs = []
5 | with open(os.path.join('data', 'val.json')) as f:
6 | seqs += json.load(f)
7 | with open(os.path.join('data', 'test.json')) as f:
8 | seqs += json.load(f)
9 |
10 | for seq in seqs:
11 | # cvpr'19 model
12 | os.system('python extract_delf.py --config_path=third_party/delf-config/delf_config_example.pbtxt --list_images_path=txt/list-{}.txt --output_dir=../benchmark-features/delf-gld-default/{}'.format(seq, seq))
13 |
--------------------------------------------------------------------------------
/run_geodesc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # geodesc
4 | echo "Extracting Geodesc"
5 |
6 | python extract_descriptors_geodesc.py --dataset_path=../benchmark-patches-8k --method_name=sift8k_8000_geodesc
7 | python extract_descriptors_geodesc.py --dataset_path=../benchmark-patches-8k-upright-no-dups --method_name=sift8k_8000_geodesc-upright-no-dups
8 |
9 | #python extract_descriptors_geodesc.py --dataset_path=../benchmark-patches-default --method_name=siftdef_2048_geodesc
10 | #python extract_descriptors_geodesc.py --dataset_path=../benchmark-patches-default-upright-no-dups --method_name=siftdef_2048_geodesc-upright-no-dups
11 |
--------------------------------------------------------------------------------
/run_hardnet.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # hardnet
4 | echo "Extracting Hardnet"
5 |
6 | python extract_descriptors_hardnet.py --dataset_path=../benchmark-patches-8k --method_name=sift8k_8000_hardnet
7 | python extract_descriptors_hardnet.py --dataset_path=../benchmark-patches-8k-upright-no-dups --method_name=sift8k_8000_hardnet-upright-no-dups
8 |
9 | #python extract_descriptors_hardnet.py --dataset_path=../benchmark-patches-default --method_name=siftdef_2048_hardnet
10 | #python extract_descriptors_hardnet.py --dataset_path=../benchmark-patches-default-upright-no-dups --method_name=siftdef_2048_hardnet-upright-no-dups
11 |
--------------------------------------------------------------------------------
/run_logpolar.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # logpolar
4 | echo "Extracting Log-polar descriptors"
5 |
6 | python extract_descriptors_logpolar.py --dataset_path=../benchmark-patches-8k --config_file=third_party/log_polar_descriptors/configs/init_one_example_ptn_96.yml --method_name=sift8k_8000_logpolar96
7 | python extract_descriptors_logpolar.py --dataset_path=../benchmark-patches-8k-upright-no-dups --config_file=third_party/log_polar_descriptors/configs/init_one_example_ptn_96.yml --method_name=sift8k_8000_logpolar96-upright-no-dups
8 |
9 | #python extract_descriptors_logpolar.py --dataset_path=../benchmark-patches-default --config_file=third_party/log_polar_descriptors/configs/init_one_example_ptn_96.yml --method_name=siftdef_2048_logpolar96
10 | #python extract_descriptors_logpolar.py --dataset_path=../benchmark-patches-default-upright-no-dups --config_file=third_party/log_polar_descriptors/configs/init_one_example_ptn_96.yml --method_name=siftdef_2048_logpolar96-upright-no-dups
11 |
--------------------------------------------------------------------------------
/run_sosnet.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # sosnet
4 | echo "Extracting SOSnet"
5 |
6 | python extract_descriptors_sosnet.py --dataset_path=../benchmark-patches-8k --method_name=sift8k_8000_sosnet
7 | python extract_descriptors_sosnet.py --dataset_path=../benchmark-patches-8k-upright-no-dups --method_name=sift8k_8000_sosnet-upright-no-dups
8 |
9 | python extract_descriptors_sosnet.py --dataset_path=../benchmark-patches-default --method_name=siftdef_2048_sosnet
10 | python extract_descriptors_sosnet.py --dataset_path=../benchmark-patches-default-upright-no-dups --method_name=siftdef_2048_sosnet-upright-no-dups
11 |
--------------------------------------------------------------------------------
/run_superpoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # superpoint
4 | echo "Extracting Superpoint"
5 | python third_party/superpoint_forked/superpoint.py --cuda --num_kp=2048 --nms_dist=4 --resize_image_to=1200 --method_name=superpoint-nms4-r1200-lowerdet --conf_thresh=0.0001
6 | python third_party/superpoint_forked/superpoint.py --cuda --num_kp=2048 --nms_dist=3 --resize_image_to=1200 --method_name=superpoint-nms3-r1200-lowerdet --conf_thresh=0.0001
7 | python third_party/superpoint_forked/superpoint.py --cuda --num_kp=2048 --nms_dist=2 --resize_image_to=1200 --method_name=superpoint-nms2-r1200-lowerdet --conf_thresh=0.0001
8 | python third_party/superpoint_forked/superpoint.py --cuda --num_kp=2048 --nms_dist=1 --resize_image_to=1200 --method_name=superpoint-nms1-r1200-lowerdet --conf_thresh=0.0001
9 |
--------------------------------------------------------------------------------
/sp_configs.py:
--------------------------------------------------------------------------------
1 | import json
2 | from copy import deepcopy
3 |
4 |
5 | pdfdict = {"lanet": "https://openaccess.thecvf.com/content/ACCV2022/papers/Wang_Rethinking_Low-level_Features_for_Interest_Point_Detection_and_Description_ACCV_2022_paper.pdf",
6 | "kp2d": "https://openreview.net/pdf?id=Skx82ySYPH",
7 | "superpoint_indep": "https://arxiv.org/abs/2007.15122",
8 | "superpoint": "https://arxiv.org/abs/1712.07629",
9 | }
10 | repo_dict = {"lanet": "https://github.com/wangch-g/lanet",
11 | "kp2d":"https://github.com/TRI-ML/KP2D",
12 | "superpoint": "https://github.com/magicleap/SuperPointPretrainedNetwork",
13 | "superpoint_indep": "https://github.com/eric-yyjau/pytorch-superpoint",
14 | }
15 | method_name_dict = {"lanet": "LANet",
16 | "kp2d": "KeypointNet",
17 | "superpoint_indep": "DeepFEPE SuperPoint",
18 | "superpoint": "MagicLeap SuperPoint"}
19 |
20 | metadata_dict = {
21 | "publish_anonymously": False,
22 | "authors": "Submitted by benchmark orgs, implementation by paper authors",
23 | "contact_email": "ducha.aiki@gmail.com",
24 | "method_name": "",
25 | "method_description":
26 | r"""Baseline, PUT 2048 features
27 | Matched using the built-in matcher (bidirectional filter with the 'both' strategy,
28 | hopefully optimal inlier and ratio test thresholds) with DEGENSAC""",
29 | "link_to_website": "",
30 | "link_to_pdf": ""
31 | }
32 |
33 | config_common_dict = {"json_label": "rootsiftfix",
34 | "keypoint": "korniadogfix",
35 | "descriptor": "rootsift",
36 | "num_keypoints": 2048}
37 |
38 |
39 | matcher_template_dict = {
40 | "method": "nn",
41 | "distance": "L2",
42 | "flann": False,
43 | "num_nn": 1,
44 | "filtering": {
45 | "type": "snn_ratio_pairwise",
46 | "threshold": 0.99,
47 | },
48 | "symmetric": {
49 | "enabled": True,
50 | "reduce": "both",
51 | }
52 | }
53 |
54 | geom_template_dict = {"method": "cmp-degensac-f",
55 | "threshold": 0.75,
56 | "confidence": 0.999999,
57 | "max_iter": 100000,
58 | "error_type": "sampson",
59 | "degeneracy_check": True,
60 | }
61 |
62 | base_config = {
63 | "metadata": metadata_dict,
64 | "config_common": config_common_dict,
65 | "config_phototourism_stereo": {
66 | "use_custom_matches": False,
67 | "matcher": deepcopy(matcher_template_dict),
68 | "outlier_filter": { "method": "none" },
69 | "geom": deepcopy(geom_template_dict)
70 | },
71 | "config_pragueparks_stereo": {
72 | "use_custom_matches": False,
73 | "matcher": deepcopy(matcher_template_dict),
74 | "outlier_filter": { "method": "none" },
75 | "geom": deepcopy(geom_template_dict)
76 | },
77 |
78 | }
79 |
80 | if __name__ == '__main__':
81 | kp = 'kp2d'
82 | for v in ['v0','v1', 'v2', 'v3', 'v4']:
83 | for norm in ['', '_norm']:
84 | json_fn = f'{kp}_{v}{norm}_1024'
85 | json_fn2 = f'{kp}_{v}{norm}_1024'.replace('_','-')
86 | md = deepcopy(metadata_dict)
87 | md['link_to_website'] = repo_dict[kp]
88 | md['link_to_pdf'] = pdfdict[kp]
89 | md['method_name'] = method_name_dict[kp]
90 | md['method_description']=md['method_description'].replace('PUT', method_name_dict[kp])
91 | common = {"json_label": json_fn2,
92 | "keypoint": json_fn2,
93 | "descriptor": json_fn2,
94 | "num_keypoints": 2048}
95 | base_config = {
96 | "metadata": md,
97 | "config_common": common,
98 | "config_phototourism_stereo": {
99 | "use_custom_matches": False,
100 | "matcher": deepcopy(matcher_template_dict),
101 | "outlier_filter": { "method": "none" },
102 | "geom": deepcopy(geom_template_dict)
103 | },
104 | "config_pragueparks_stereo": {
105 | "use_custom_matches": False,
106 | "matcher": deepcopy(matcher_template_dict),
107 | "outlier_filter": { "method": "none" },
108 | "geom": deepcopy(geom_template_dict)
109 | }
110 | }
111 | with open(f'OUT_JSON/{json_fn}.json', 'w') as f:
112 | json.dump([base_config], f, indent=2)
113 | match_ths = [0.85, 0.9, 0.95, 0.99]
114 | inl_ths = [0.5, 0.75, 1.0, 1.25]
115 | configs = []
116 | for match_th in match_ths:
117 | for inl_th in inl_ths:
118 | current_config = deepcopy(base_config)
119 | for dset in ['phototourism', 'pragueparks']:
120 | current_config[f'config_{dset}_stereo']['geom']['threshold'] = inl_th
121 | current_config[f'config_{dset}_stereo']['matcher']['filtering']['threshold'] = match_th
122 | label = current_config['config_common']['json_label']
123 | current_config['config_common']['json_label'] = f'{label}-snnth-{match_th}-inlth-{inl_th}'
124 | configs.append(current_config)
125 | with open(f'OUT_JSON_RANSAC/{json_fn}_tuning.json', 'w') as f:
126 | json.dump(configs, f, indent=2)
127 | kp = 'superpoint'
128 | for v in ['_magicleap']:
129 | for norm in ['', '_norm']:
130 | json_fn = f'{kp}{v}{norm}_1024'
131 | json_fn2 = f'{kp}{v}{norm}_1024'.replace('_','-')
132 | md = deepcopy(metadata_dict)
133 | md['link_to_website'] = repo_dict[kp]
134 | md['link_to_pdf'] = pdfdict[kp]
135 | md['method_name'] = method_name_dict[kp]
136 | md['method_description']=md['method_description'].replace('PUT', method_name_dict[kp])
137 | common = {"json_label": json_fn2,
138 | "keypoint": json_fn2,
139 | "descriptor": json_fn2,
140 | "num_keypoints": 2048}
141 | base_config = {
142 | "metadata": md,
143 | "config_common": common,
144 | "config_phototourism_stereo": {
145 | "use_custom_matches": False,
146 | "matcher": deepcopy(matcher_template_dict),
147 | "outlier_filter": { "method": "none" },
148 | "geom": deepcopy(geom_template_dict)
149 | },
150 | "config_pragueparks_stereo": {
151 | "use_custom_matches": False,
152 | "matcher": deepcopy(matcher_template_dict),
153 | "outlier_filter": { "method": "none" },
154 | "geom": deepcopy(geom_template_dict)
155 | }
156 | }
157 | with open(f'OUT_JSON/{json_fn}.json', 'w') as f:
158 | json.dump([base_config], f, indent=2)
159 | match_ths = [0.85, 0.9, 0.95, 0.99]
160 | inl_ths = [0.5, 0.75, 1.0, 1.25, 1.5]
161 | configs = []
162 | for match_th in match_ths:
163 | for inl_th in inl_ths:
164 | current_config = deepcopy(base_config)
165 | for dset in ['phototourism', 'pragueparks']:
166 | current_config[f'config_{dset}_stereo']['geom']['threshold'] = inl_th
167 | current_config[f'config_{dset}_stereo']['matcher']['filtering']['threshold'] = match_th
168 | label = current_config['config_common']['json_label']
169 | current_config['config_common']['json_label'] = f'{label}-snnth-{match_th}-inlth-{inl_th}'
170 | configs.append(current_config)
171 | with open(f'OUT_JSON_RANSAC/{json_fn}_tuning.json', 'w') as f:
172 | json.dump(configs, f, indent=2)
173 | kp = 'lanet'
174 | for v in ['v0','v1']:
175 | for norm in ['', '_norm']:
176 | json_fn = f'{kp}_{v}{norm}_1024'
177 | json_fn2 = f'{kp}_{v}{norm}_1024'.replace('_','-')
178 | md = deepcopy(metadata_dict)
179 | md['link_to_website'] = repo_dict[kp]
180 | md['link_to_pdf'] = pdfdict[kp]
181 | md['method_name'] = method_name_dict[kp]
182 | md['method_description']=md['method_description'].replace('PUT', method_name_dict[kp])
183 | common = {"json_label": json_fn2,
184 | "keypoint": json_fn2,
185 | "descriptor": json_fn2,
186 | "num_keypoints": 2048}
187 | base_config = {
188 | "metadata": md,
189 | "config_common": common,
190 | "config_phototourism_stereo": {
191 | "use_custom_matches": False,
192 | "matcher": deepcopy(matcher_template_dict),
193 | "outlier_filter": { "method": "none" },
194 | "geom": deepcopy(geom_template_dict)
195 | },
196 | "config_pragueparks_stereo": {
197 | "use_custom_matches": False,
198 | "matcher": deepcopy(matcher_template_dict),
199 | "outlier_filter": { "method": "none" },
200 | "geom": deepcopy(geom_template_dict)
201 | }
202 | }
203 | with open(f'OUT_JSON/{json_fn}.json', 'w') as f:
204 | json.dump([base_config], f, indent=2)
205 | match_ths = [0.85, 0.9, 0.95, 0.99]
206 | inl_ths = [0.5, 0.75, 1.0, 1.25]
207 | configs = []
208 | for match_th in match_ths:
209 | for inl_th in inl_ths:
210 | current_config = deepcopy(base_config)
211 | for dset in ['phototourism', 'pragueparks']:
212 | current_config[f'config_{dset}_stereo']['geom']['threshold'] = inl_th
213 | current_config[f'config_{dset}_stereo']['matcher']['filtering']['threshold'] = match_th
214 | label = current_config['config_common']['json_label']
215 | current_config['config_common']['json_label'] = f'{label}-snnth-{match_th}-inlth-{inl_th}'
216 | configs.append(current_config)
217 | with open(f'OUT_JSON_RANSAC/{json_fn}_tuning.json', 'w') as f:
218 | json.dump(configs, f, indent=2)
219 | kp = 'superpoint_indep'
220 | for v in ['coco', 'kitty']:
221 | for norm in ['', '_norm']:
222 | for sp in ['', '_subpix']:
223 | json_fn = f'{kp}_{v}{norm}_1024{sp}'
224 | json_fn2 = f'{kp}_{v}{norm}_1024{sp}'.replace('_','-')
225 | md = deepcopy(metadata_dict)
226 | md['link_to_website'] = repo_dict[kp]
227 | md['link_to_pdf'] = pdfdict[kp]
228 | md['method_name'] = method_name_dict[kp]
229 | md['method_description']=md['method_description'].replace('PUT', method_name_dict[kp])
230 | common = {"json_label": json_fn2,
231 | "keypoint": json_fn2,
232 | "descriptor": json_fn2,
233 | "num_keypoints": 2048}
234 | base_config = {
235 | "metadata": md,
236 | "config_common": common,
237 | "config_phototourism_stereo": {
238 | "use_custom_matches": False,
239 | "matcher": deepcopy(matcher_template_dict),
240 | "outlier_filter": { "method": "none" },
241 | "geom": deepcopy(geom_template_dict)
242 | },
243 | "config_pragueparks_stereo": {
244 | "use_custom_matches": False,
245 | "matcher": deepcopy(matcher_template_dict),
246 | "outlier_filter": { "method": "none" },
247 | "geom": deepcopy(geom_template_dict)
248 | }
249 | }
250 | with open(f'OUT_JSON/{json_fn}.json', 'w') as f:
251 | json.dump([base_config], f, indent=2)
252 | match_ths = [0.85, 0.9, 0.95, 0.99]
253 | inl_ths = [0.5, 0.75, 1.0, 1.25]
254 | configs = []
255 | for match_th in match_ths:
256 | for inl_th in inl_ths:
257 | current_config = deepcopy(base_config)
258 | for dset in ['phototourism', 'pragueparks']:
259 | current_config[f'config_{dset}_stereo']['geom']['threshold'] = inl_th
260 | current_config[f'config_{dset}_stereo']['matcher']['filtering']['threshold'] = match_th
261 | label = current_config['config_common']['json_label']
262 | current_config['config_common']['json_label'] = f'{label}-snnth-{match_th}-inlth-{inl_th}'
263 | configs.append(current_config)
264 | with open(f'OUT_JSON_RANSAC/{json_fn}_tuning.json', 'w') as f:
265 | json.dump(configs, f, indent=2)
266 |
267 |
--------------------------------------------------------------------------------
/system/geodesc.yml:
--------------------------------------------------------------------------------
1 | name: geodesc
2 | channels:
3 | - defaults
4 | dependencies:
5 | - _libgcc_mutex=0.1=main
6 | - _tflow_select=2.1.0=gpu
7 | - absl-py=0.4.1=py35_0
8 | - astor=0.7.1=py35_0
9 | - blas=1.0=mkl
10 | - bzip2=1.0.8=h7b6447c_0
11 | - ca-certificates=2019.8.28=0
12 | - cairo=1.14.12=h8948797_3
13 | - certifi=2018.8.24=py35_1
14 | - cudatoolkit=9.2=0
15 | - cudnn=7.6.0=cuda9.2_0
16 | - cupti=9.2.148=0
17 | - ffmpeg=4.0=hcdf2ecd_0
18 | - fontconfig=2.13.0=h9420a91_0
19 | - freeglut=3.0.0=hf484d3e_5
20 | - freetype=2.9.1=h8a8886c_1
21 | - gast=0.3.2=py_0
22 | - glib=2.56.2=hd408876_0
23 | - graphite2=1.3.13=h23475e2_0
24 | - grpcio=1.12.1=py35hdbcaa40_0
25 | - h5py=2.8.0=py35h989c5e5_3
26 | - harfbuzz=1.8.8=hffaf4a1_0
27 | - hdf5=1.10.2=hba1933b_1
28 | - icu=58.2=h9c2bf20_1
29 | - intel-openmp=2019.4=243
30 | - jasper=2.0.14=h07fcdf6_1
31 | - jpeg=9b=h024ee3a_2
32 | - libedit=3.1.20181209=hc058e9b_0
33 | - libffi=3.2.1=hd88cf55_4
34 | - libgcc-ng=9.1.0=hdf63c60_0
35 | - libgfortran-ng=7.3.0=hdf63c60_0
36 | - libglu=9.0.0=hf484d3e_1
37 | - libopencv=3.4.2=hb342d67_1
38 | - libopus=1.3=h7b6447c_0
39 | - libpng=1.6.37=hbc83047_0
40 | - libprotobuf=3.6.0=hdbcaa40_0
41 | - libstdcxx-ng=9.1.0=hdf63c60_0
42 | - libtiff=4.0.10=h2733197_2
43 | - libuuid=1.0.3=h1bed415_2
44 | - libvpx=1.7.0=h439df22_0
45 | - libxcb=1.13=h1bed415_1
46 | - libxml2=2.9.9=hea5a465_1
47 | - markdown=2.6.11=py35_0
48 | - mkl=2019.4=243
49 | - ncurses=6.1=he6710b0_1
50 | - numpy=1.14.2=py35hdbf6ddf_0
51 | - opencv=3.4.2=py35h6fd60c2_1
52 | - openssl=1.0.2t=h7b6447c_1
53 | - pcre=8.43=he6710b0_0
54 | - pip=10.0.1=py35_0
55 | - pixman=0.38.0=h7b6447c_0
56 | - protobuf=3.6.0=py35hf484d3e_0
57 | - py-opencv=3.4.2=py35hb342d67_1
58 | - python=3.5.6=hc3d631a_0
59 | - readline=7.0=h7b6447c_5
60 | - setuptools=40.2.0=py35_0
61 | - six=1.11.0=py35_1
62 | - sqlite=3.29.0=h7b6447c_0
63 | - tensorboard=1.10.0=py35hf484d3e_0
64 | - tensorflow=1.10.0=gpu_py35hd9c640d_0
65 | - tensorflow-base=1.10.0=gpu_py35had579c0_0
66 | - tensorflow-gpu=1.10.0=hf154084_0
67 | - termcolor=1.1.0=py35_1
68 | - tk=8.6.8=hbc83047_0
69 | - tqdm=4.36.1=py_0
70 | - werkzeug=0.16.0=py_0
71 | - wheel=0.31.1=py35_0
72 | - xz=5.2.4=h14c3975_4
73 | - zlib=1.2.11=h7b6447c_3
74 | - zstd=1.3.7=h0b5b093_0
75 | prefix: /home/trulls/miniconda3/envs/geodesc
76 |
77 |
--------------------------------------------------------------------------------
/system/hardnet.yml:
--------------------------------------------------------------------------------
1 | name: hardnet
2 | channels:
3 | - anaconda
4 | - pytorch
5 | - conda-forge
6 | - defaults
7 | dependencies:
8 | - _libgcc_mutex=0.1=main
9 | - backcall=0.1.0=py35_0
10 | - blas=1.0=mkl
11 | - bzip2=1.0.8=h7b6447c_0
12 | - ca-certificates=2019.11.28=hecc5488_0
13 | - cairo=1.14.12=h8948797_3
14 | - certifi=2016.9.26=py35_0
15 | - cffi=1.11.5=py35he75722e_1
16 | - cudatoolkit=10.0.130=0
17 | - cycler=0.10.0=py35hc4d5149_0
18 | - dbus=1.13.6=h746ee38_0
19 | - decorator=4.4.0=py_0
20 | - expat=2.2.6=he6710b0_0
21 | - ffmpeg=4.0=hcdf2ecd_0
22 | - fontconfig=2.13.0=h9420a91_0
23 | - freeglut=3.0.0=hf484d3e_5
24 | - freetype=2.9.1=h8a8886c_1
25 | - glib=2.56.2=hd408876_0
26 | - graphite2=1.3.13=h23475e2_0
27 | - gst-plugins-base=1.14.0=hbbd80ab_1
28 | - gstreamer=1.14.0=hb453b48_1
29 | - h5py=2.8.0=py35h989c5e5_3
30 | - harfbuzz=1.8.8=hffaf4a1_0
31 | - hdf5=1.10.2=hba1933b_1
32 | - icu=58.2=h9c2bf20_1
33 | - intel-openmp=2019.4=243
34 | - ipython=6.5.0=py35_0
35 | - ipython_genutils=0.2.0=py35hc9e07d0_0
36 | - jasper=2.0.14=h07fcdf6_1
37 | - jedi=0.12.1=py35_0
38 | - jpeg=9b=h024ee3a_2
39 | - kiwisolver=1.0.1=py35hf484d3e_0
40 | - libedit=3.1.20181209=hc058e9b_0
41 | - libffi=3.2.1=hd88cf55_4
42 | - libgcc-ng=9.1.0=hdf63c60_0
43 | - libgfortran-ng=7.3.0=hdf63c60_0
44 | - libglu=9.0.0=hf484d3e_1
45 | - libopencv=3.4.2=hb342d67_1
46 | - libopus=1.3=h7b6447c_0
47 | - libpng=1.6.37=hbc83047_0
48 | - libstdcxx-ng=9.1.0=hdf63c60_0
49 | - libtiff=4.0.10=h2733197_2
50 | - libuuid=1.0.3=h1bed415_2
51 | - libvpx=1.7.0=h439df22_0
52 | - libxcb=1.13=h1bed415_1
53 | - libxml2=2.9.9=hea5a465_1
54 | - mkl=2018.0.3=1
55 | - ncurses=6.1=he6710b0_1
56 | - ninja=1.8.2=py35h6bb024c_1
57 | - numpy=1.14.2=py35hdbf6ddf_0
58 | - olefile=0.46=py35_0
59 | - opencv=3.4.2=py35h6fd60c2_1
60 | - openssl=1.0.2u=h516909a_0
61 | - pandas=0.23.4=py35h04863e7_0
62 | - parso=0.5.1=py_0
63 | - patsy=0.5.0=py35_0
64 | - pcre=8.43=he6710b0_0
65 | - pexpect=4.6.0=py35_0
66 | - pickleshare=0.7.4=py35hd57304d_0
67 | - pillow=5.2.0=py35heded4f4_0
68 | - pip=10.0.1=py35_0
69 | - pixman=0.38.0=h7b6447c_0
70 | - progressbar2=3.47.0=py_0
71 | - prompt_toolkit=1.0.15=py35hc09de7a_0
72 | - ptyprocess=0.6.0=py35_0
73 | - py=1.8.1=py_0
74 | - py-opencv=3.4.2=py35hb342d67_1
75 | - pycparser=2.19=py35_0
76 | - pygments=2.4.2=py_0
77 | - pyparsing=2.4.2=py_0
78 | - pyqt=5.9.2=py35h05f1152_2
79 | - pytest=3.2.2=py_0
80 | - pytest-runner=5.2=py_0
81 | - python=3.5.6=hc3d631a_0
82 | - python-dateutil=2.7.3=py35_0
83 | - python-utils=2.3.0=py_1
84 | - pytorch=1.2.0=py3.5_cuda10.0.130_cudnn7.6.2_0
85 | - pytz=2019.2=py_0
86 | - qt=5.9.6=h8703b6f_2
87 | - readline=7.0=h7b6447c_5
88 | - scipy=1.1.0=py35hd20e5f9_0
89 | - seaborn=0.9.0=py35_0
90 | - setuptools=40.2.0=py35_0
91 | - simplegeneric=0.8.1=py35_2
92 | - sip=4.19.8=py35hf484d3e_0
93 | - six=1.11.0=py35_1
94 | - sqlite=3.29.0=h7b6447c_0
95 | - statsmodels=0.9.0=py35h3010b51_0
96 | - tk=8.6.8=hbc83047_0
97 | - torchvision=0.4.0=py35_cu100
98 | - tornado=5.1.1=py35h7b6447c_0
99 | - tqdm=4.36.1=py_0
100 | - traitlets=4.3.2=py35ha522a97_0
101 | - wcwidth=0.1.7=py35hcd08066_0
102 | - wheel=0.31.1=py35_0
103 | - xz=5.2.4=h14c3975_4
104 | - yaml=0.1.7=h96e3832_1
105 | - zlib=1.2.11=h7b6447c_3
106 | - zstd=1.3.7=h0b5b093_0
107 | - pip:
108 | - absl-py==0.8.0
109 | - astor==0.8.0
110 | - autopep8==1.4.4
111 | - gast==0.3.2
112 | - google-pasta==0.1.7
113 | - grpcio==1.24.0
114 | - imageio==2.5.0
115 | - keras-applications==1.0.8
116 | - keras-preprocessing==1.1.0
117 | - markdown==3.1.1
118 | - matplotlib==3.0.3
119 | - networkx==2.3
120 | - protobuf==3.9.2
121 | - pycodestyle==2.5.0
122 | - pywavelets==1.0.3
123 | - pyyaml==5.1.2
124 | - scikit-image==0.15.0
125 | - tensorboard==1.14.0
126 | - tensorflow-estimator==1.14.0
127 | - tensorflow-gpu==1.14.0
128 | - termcolor==1.1.0
129 | - werkzeug==0.16.0
130 | - wrapt==1.11.2
131 | - yacs==0.1.6
132 | - extract_patches
133 | prefix: /home/yuhe/anaconda3/envs/hardnet
134 |
135 |
--------------------------------------------------------------------------------
/system/lfnet.yml:
--------------------------------------------------------------------------------
1 | name: lfnet
2 | channels:
3 | - conda-forge
4 | - defaults
5 | dependencies:
6 | - _libgcc_mutex=0.1=conda_forge
7 | - _openmp_mutex=4.5=0_gnu
8 | - _tflow_select=2.1.0=gpu
9 | - absl-py=0.9.0=py36_0
10 | - astor=0.7.1=py_0
11 | - blas=1.1=openblas
12 | - bzip2=1.0.8=h516909a_2
13 | - c-ares=1.15.0=h516909a_1001
14 | - ca-certificates=2019.11.28=hecc5488_0
15 | - cairo=1.16.0=hfb77d84_1002
16 | - certifi=2019.11.28=py36h9f0ad1d_1
17 | - dbus=1.13.6=he372182_0
18 | - expat=2.2.9=he1b5a44_2
19 | - ffmpeg=4.1.3=h167e202_0
20 | - fontconfig=2.13.1=h86ecdb6_1001
21 | - freetype=2.10.0=he983fc9_1
22 | - gast=0.3.3=py_0
23 | - gettext=0.19.8.1=hc5be6a0_1002
24 | - giflib=5.2.1=h516909a_2
25 | - glib=2.58.3=py36h6f030ca_1002
26 | - gmp=6.2.0=he1b5a44_2
27 | - gnutls=3.6.5=hd3a4fd2_1002
28 | - graphite2=1.3.13=hf484d3e_1000
29 | - grpcio=1.23.0=py36he9ae1f9_0
30 | - gst-plugins-base=1.14.5=h0935bb2_2
31 | - gstreamer=1.14.5=h36ae1b5_2
32 | - h5py=2.9.0=nompi_py36hf008753_1102
33 | - harfbuzz=2.4.0=h9f30f68_3
34 | - hdf5=1.10.4=nompi_h3c11f04_1106
35 | - icu=64.2=he1b5a44_1
36 | - imageio=2.8.0=py_0
37 | - jasper=1.900.1=h07fcdf6_1006
38 | - jpeg=9c=h14c3975_1001
39 | - keras-applications=1.0.8=py_1
40 | - keras-preprocessing=1.1.0=py_0
41 | - lame=3.100=h14c3975_1001
42 | - ld_impl_linux-64=2.33.1=h53a641e_8
43 | - libblas=3.8.0=11_openblas
44 | - libcblas=3.8.0=11_openblas
45 | - libffi=3.2.1=he1b5a44_1006
46 | - libgcc-ng=9.2.0=h24d8f2e_2
47 | - libgfortran=3.0.0=1
48 | - libgfortran-ng=7.3.0=hdf63c60_5
49 | - libgomp=9.2.0=h24d8f2e_2
50 | - libiconv=1.15=h516909a_1005
51 | - liblapack=3.8.0=11_openblas
52 | - liblapacke=3.8.0=11_openblas
53 | - libopenblas=0.3.6=h5a2b251_2
54 | - libpng=1.6.37=hed695b0_0
55 | - libprotobuf=3.11.3=h8b12597_0
56 | - libstdcxx-ng=9.2.0=hdf63c60_2
57 | - libtiff=4.1.0=hc3755c2_3
58 | - libuuid=2.32.1=h14c3975_1000
59 | - libwebp=1.0.2=h56121f0_5
60 | - libxcb=1.13=h14c3975_1002
61 | - libxml2=2.9.10=hee79883_0
62 | - lz4-c=1.8.3=he1b5a44_1001
63 | - markdown=3.2.1=py_0
64 | - mock=3.0.5=py36_0
65 | - ncurses=6.1=hf484d3e_1002
66 | - nettle=3.4.1=h1bed415_1002
67 | - numpy=1.14.5=py36_blas_openblashd3ea46f_202
68 | - olefile=0.46=py_0
69 | - openblas=0.2.20=8
70 | - opencv=4.1.0=py36h4a2692f_3
71 | - openh264=1.8.0=hdbcaa40_1000
72 | - openssl=1.1.1d=h516909a_0
73 | - pandas=0.24.2=py36hf484d3e_0
74 | - pcre=8.44=he1b5a44_0
75 | - pillow=7.0.0=py36h8328e55_1
76 | - pip=20.0.2=py_2
77 | - pixman=0.38.0=h516909a_1003
78 | - protobuf=3.11.3=py36he1b5a44_0
79 | - pthread-stubs=0.4=h14c3975_1001
80 | - python=3.6.10=h9d8adfe_1009_cpython
81 | - python-dateutil=2.8.1=py_0
82 | - python_abi=3.6=1_cp36m
83 | - pytz=2019.3=py_0
84 | - qt=5.9.7=h0c104cb_3
85 | - readline=8.0=hf8c457e_0
86 | - scipy=1.2.1=py36h09a28d5_1
87 | - setuptools=46.0.0=py36h9f0ad1d_2
88 | - six=1.14.0=py_1
89 | - sqlite=3.30.1=hcee41ef_0
90 | - tensorboard=1.13.1=py36_0
91 | - tensorflow=1.13.1=py36h90a7d86_1
92 | - tensorflow-estimator=1.13.0=py_0
93 | - tensorflow-gpu=1.13.1=h0d30ee6_0
94 | - termcolor=1.1.0=py_2
95 | - tk=8.6.10=hed695b0_0
96 | - tqdm=4.43.0=py_0
97 | - werkzeug=1.0.0=py_0
98 | - wheel=0.34.2=py_1
99 | - x264=1!152.20180806=h14c3975_0
100 | - xorg-kbproto=1.0.7=h14c3975_1002
101 | - xorg-libice=1.0.10=h516909a_0
102 | - xorg-libsm=1.2.3=h84519dc_1000
103 | - xorg-libx11=1.6.9=h516909a_0
104 | - xorg-libxau=1.0.9=h14c3975_0
105 | - xorg-libxdmcp=1.1.3=h516909a_0
106 | - xorg-libxext=1.3.4=h516909a_0
107 | - xorg-libxrender=0.9.10=h516909a_1002
108 | - xorg-renderproto=0.11.1=h14c3975_1002
109 | - xorg-xextproto=7.3.0=h14c3975_1002
110 | - xorg-xproto=7.0.31=h14c3975_1007
111 | - xz=5.2.4=h14c3975_1001
112 | - zlib=1.2.11=h516909a_1006
113 | - zstd=1.4.4=h3b9ef0a_1
114 | prefix: /home/yuhe/anaconda3/envs/lfnet
115 |
116 |
--------------------------------------------------------------------------------
/system/r2d2-python3.6.yml:
--------------------------------------------------------------------------------
1 | name: r2d2-python3.6
2 | channels:
3 | - pytorch
4 | - defaults
5 | dependencies:
6 | - _libgcc_mutex=0.1=main
7 | - backcall=0.1.0=py37_0
8 | - blas=1.0=mkl
9 | - bzip2=1.0.8=h7b6447c_0
10 | - ca-certificates=2020.1.1=0
11 | - cairo=1.14.12=h8948797_3
12 | - certifi=2019.11.28=py37_0
13 | - cffi=1.13.2=py37h2e261b9_0
14 | - cudatoolkit=10.0.130=0
15 | - cycler=0.10.0=py37_0
16 | - dbus=1.13.12=h746ee38_0
17 | - decorator=4.4.1=py_0
18 | - expat=2.2.6=he6710b0_0
19 | - ffmpeg=4.0=hcdf2ecd_0
20 | - fontconfig=2.13.0=h9420a91_0
21 | - freeglut=3.0.0=hf484d3e_5
22 | - freetype=2.9.1=h8a8886c_1
23 | - glib=2.63.1=h5a9c865_0
24 | - graphite2=1.3.13=h23475e2_0
25 | - gst-plugins-base=1.14.0=hbbd80ab_1
26 | - gstreamer=1.14.0=hb453b48_1
27 | - h5py=2.8.0=py37h989c5e5_3
28 | - harfbuzz=1.8.8=hffaf4a1_0
29 | - hdf5=1.10.2=hba1933b_1
30 | - icu=58.2=h9c2bf20_1
31 | - intel-openmp=2019.4=243
32 | - ipython=7.10.2=py37h39e3cac_0
33 | - ipython_genutils=0.2.0=py37_0
34 | - jasper=2.0.14=h07fcdf6_1
35 | - jedi=0.15.1=py37_0
36 | - jpeg=9b=h024ee3a_2
37 | - kiwisolver=1.1.0=py37he6710b0_0
38 | - libedit=3.1.20181209=hc058e9b_0
39 | - libffi=3.2.1=hd88cf55_4
40 | - libgcc-ng=9.1.0=hdf63c60_0
41 | - libgfortran-ng=7.3.0=hdf63c60_0
42 | - libglu=9.0.0=hf484d3e_1
43 | - libopencv=3.4.2=hb342d67_1
44 | - libopus=1.3=h7b6447c_0
45 | - libpng=1.6.37=hbc83047_0
46 | - libstdcxx-ng=9.1.0=hdf63c60_0
47 | - libtiff=4.1.0=h2733197_0
48 | - libuuid=1.0.3=h1bed415_2
49 | - libvpx=1.7.0=h439df22_0
50 | - libxcb=1.13=h1bed415_1
51 | - libxml2=2.9.9=hea5a465_1
52 | - matplotlib=3.1.1=py37h5429711_0
53 | - mkl=2019.4=243
54 | - mkl-service=2.3.0=py37he904b0f_0
55 | - mkl_fft=1.0.15=py37ha843d7b_0
56 | - mkl_random=1.1.0=py37hd6b4f25_0
57 | - ncurses=6.1=he6710b0_1
58 | - ninja=1.9.0=py37hfd86e86_0
59 | - numpy=1.17.4=py37hc1035e2_0
60 | - numpy-base=1.17.4=py37hde5b4d6_0
61 | - olefile=0.46=py_0
62 | - opencv=3.4.2=py37h6fd60c2_1
63 | - openssl=1.1.1d=h7b6447c_4
64 | - parso=0.5.2=py_0
65 | - pcre=8.43=he6710b0_0
66 | - pexpect=4.7.0=py37_0
67 | - pickleshare=0.7.5=py37_0
68 | - pillow=6.2.1=py37h34e0f95_0
69 | - pip=19.3.1=py37_0
70 | - pixman=0.38.0=h7b6447c_0
71 | - prompt_toolkit=3.0.2=py_0
72 | - ptyprocess=0.6.0=py37_0
73 | - py-opencv=3.4.2=py37hb342d67_1
74 | - pycparser=2.19=py_0
75 | - pygments=2.5.2=py_0
76 | - pyparsing=2.4.5=py_0
77 | - pyqt=5.9.2=py37h05f1152_2
78 | - python=3.7.5=h0371630_0
79 | - python-dateutil=2.8.1=py_0
80 | - pytz=2019.3=py_0
81 | - qt=5.9.7=h5867ecd_1
82 | - readline=7.0=h7b6447c_5
83 | - scipy=1.3.2=py37h7c811a0_0
84 | - setuptools=42.0.2=py37_0
85 | - sip=4.19.8=py37hf484d3e_0
86 | - six=1.13.0=py37_0
87 | - sqlite=3.30.1=h7b6447c_0
88 | - tk=8.6.8=hbc83047_0
89 | - tornado=6.0.3=py37h7b6447c_0
90 | - tqdm=4.40.2=py_0
91 | - traitlets=4.3.3=py37_0
92 | - wcwidth=0.1.7=py37_0
93 | - wheel=0.33.6=py37_0
94 | - xz=5.2.4=h14c3975_4
95 | - zlib=1.2.11=h7b6447c_3
96 | - zstd=1.3.7=h0b5b093_0
97 | - pytorch=1.3.1=py3.7_cuda10.0.130_cudnn7.6.3_0
98 | - torchvision=0.4.2=py37_cu100
99 | - pip:
100 | - extract-patches==0.1.1
101 | - torch==1.3.1
102 | prefix: /home/trulls/miniconda3/envs/r2d2-python3.6
103 |
104 |
--------------------------------------------------------------------------------
/third_party/l2net_config/convert_l2net_weights_matconv_pytorch.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import scipy.io as sio
3 | import torch
4 | import torch.nn.init
5 | from misc.l2net.l2net_model import L2Net
6 |
7 | eps = 1e-10
8 |
9 | def check_ported(l2net_model, test_patch, img_mean):
10 |
11 | test_patch = test_patch.transpose(3, 2, 0, 1)-img_mean
12 | desc = l2net_model(torch.from_numpy(test_patch))
13 | print(desc)
14 | return desc
15 |
16 | if __name__ == '__main__':
17 |
18 | path_to_l2net_weights = '/cvlabsrc1/cvlab/datasets_anastasiia/descriptors/sfm-evaluation-benchmarking/third_party/l2net/matlab/L2Net-LIB+.mat'
19 |
20 | l2net_weights = sio.loadmat(path_to_l2net_weights)
21 |
22 | l2net_model = L2Net()
23 | l2net_model.eval()
24 |
25 | new_state_dict = l2net_model.state_dict().copy()
26 |
27 | conv_layers, bn_layers = {}, {}
28 | all_layer_weights = l2net_weights['net']['layers'][0][0][0]
29 | img_mean = l2net_weights['pixMean']
30 | conv_layers_to_track, bn_layers_to_track = [0,3,6,9,12,15,18], \
31 | [1,4,7,10,13,16,19]
32 | conv_i, bn_i = 0,0
33 |
34 | for layer in all_layer_weights:
35 |
36 | if 'weights' not in layer.dtype.names:
37 | continue
38 | layer_name = layer[0][0][0][0]
39 | layer_value = layer['weights'][0][0][0]
40 | if layer_name == 'conv':
41 | conv_layers[conv_layers_to_track[conv_i]] = layer_value
42 | conv_i+=1
43 | elif layer_name == 'bnormPair':
44 | bn_layers[bn_layers_to_track[bn_i]] = layer_value
45 | bn_i+=1
46 |
47 | for key, value in new_state_dict.items():
48 | layer_number = int(key.split('.')[1])
49 | if layer_number in conv_layers.keys():
50 | if 'weight' in key:
51 | new_state_dict[key] = torch.from_numpy(conv_layers[layer_number][0].transpose((3,2,0,1)))
52 | elif 'bias' in key:
53 | new_state_dict[key] = torch.from_numpy(conv_layers[layer_number][1]).squeeze()
54 | elif layer_number in bn_layers.keys():
55 | if 'running_mean' in key:
56 | new_state_dict[key] = torch.from_numpy(np.array([x[0] for x in bn_layers[layer_number][2]])).squeeze()
57 | elif 'running_var' in key:
58 | new_state_dict[key] = torch.from_numpy(np.array([x[1] for x in bn_layers[layer_number][2]] )** 2 -eps).squeeze()
59 | elif 'weight' in key:
60 | new_state_dict[key] = torch.from_numpy(np.ones(value.size()[0])).squeeze()
61 |
62 | else:
63 | continue
64 |
65 | l2net_model.load_state_dict(new_state_dict)
66 | l2net_model.eval()
67 |
68 | torch.save(l2net_model.state_dict(),'l2net_ported_weights_lib+.pth')
69 |
70 | # compare desc on test patch with matlab implementation
71 | # test_patch_batch = sio.loadmat('test_batch_img.mat')['testPatch']
72 | # check_ported(l2net_model, test_patch_batch, img_mean)
73 | #
74 | # test_patch_one = sio.loadmat('test_one.mat')['testPatch']
75 | # check_ported(l2net_model, np.expand_dims(np.expand_dims(test_patch_one, axis=2),axis=2), img_mean)
--------------------------------------------------------------------------------
/third_party/l2net_config/l2net_model.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn.init
3 | import torch.nn as nn
4 |
5 | eps = 1e-10
6 |
7 | class L2Norm(nn.Module):
8 |
9 | def __init__(self):
10 | super(L2Norm,self).__init__()
11 | self.eps = 1e-10
12 |
13 | def forward(self, x):
14 | norm = torch.sqrt(torch.sum(x * x, dim = 1) + self.eps)
15 | x= x / norm.unsqueeze(-1).expand_as(x)
16 | return x
17 |
18 | class L2Net(nn.Module):
19 |
20 | def __init__(self):
21 | super(L2Net, self).__init__()
22 | self.features = nn.Sequential(
23 | nn.Conv2d(1, 32, kernel_size=3, padding=1, bias=True),
24 | nn.BatchNorm2d(32, affine=True, eps=eps),
25 | nn.ReLU(),
26 | nn.Conv2d(32, 32, kernel_size=3, padding=1, bias=True),
27 | nn.BatchNorm2d(32, affine=True, eps=eps),
28 | nn.ReLU(),
29 | nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1, bias=True),
30 | nn.BatchNorm2d(64, affine=True, eps=eps),
31 | nn.ReLU(),
32 | nn.Conv2d(64, 64, kernel_size=3, padding=1, bias=True),
33 | nn.BatchNorm2d(64, affine=True, eps=eps),
34 | nn.ReLU(),
35 | nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1, bias=True),
36 | nn.BatchNorm2d(128, affine=True, eps=eps),
37 | nn.ReLU(),
38 | nn.Conv2d(128, 128, kernel_size=3, padding=1, bias=True),
39 | nn.BatchNorm2d(128, affine=True, eps=eps),
40 | nn.ReLU(),
41 | nn.Conv2d(128, 128, kernel_size=8, bias=True),
42 | nn.BatchNorm2d(128, affine=True, eps=eps),
43 | )
44 | return
45 |
46 | def input_norm(self, x):
47 | # matlab norm
48 | z = x.contiguous().transpose(2, 3).contiguous().view(x.size(0),-1)
49 | x_minus_mean = z.transpose(0,1)-z.mean(1)
50 | sp = torch.std(z,1).detach()
51 | norm_inp = x_minus_mean/(sp+1e-12)
52 | norm_inp = norm_inp.transpose(0, 1).view(-1, 1, x.size(2), x.size(3)).transpose(2,3)
53 | return norm_inp
54 |
55 | def forward(self, input):
56 | norm_img = self.input_norm(input)
57 | x_features = self.features(norm_img)
58 | return nn.LocalResponseNorm(256,1*256,0.5,0.5)(x_features).view(input.size(0),-1)
59 |
--------------------------------------------------------------------------------
/third_party/l2net_config/l2net_ported_weights_lib+.pth:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubc-vision/image-matching-benchmark-baselines/35dcd21c889583a22a4f612389a6b0d7c5af506b/third_party/l2net_config/l2net_ported_weights_lib+.pth
--------------------------------------------------------------------------------
/third_party/l2net_config/test_batch_img.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubc-vision/image-matching-benchmark-baselines/35dcd21c889583a22a4f612389a6b0d7c5af506b/third_party/l2net_config/test_batch_img.mat
--------------------------------------------------------------------------------
/third_party/l2net_config/test_one.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubc-vision/image-matching-benchmark-baselines/35dcd21c889583a22a4f612389a6b0d7c5af506b/third_party/l2net_config/test_one.mat
--------------------------------------------------------------------------------
/third_party/superpoint_forked/LICENSE:
--------------------------------------------------------------------------------
1 | SUPERPOINT: SELF-SUPERVISED INTEREST POINT DETECTION AND DESCRIPTION
2 | SOFTWARE LICENSE AGREEMENT
3 | ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY
4 |
5 | BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR DOWNLOAD THE SOFTWARE.
6 |
7 | This is a license agreement ("Agreement") between your academic institution or non-profit organization or self (called "Licensee" or "You" in this Agreement) and Magic Leap, Inc. (called "Licensor" in this Agreement). All rights not specifically granted to you in this Agreement are reserved for Licensor.
8 |
9 | RESERVATION OF OWNERSHIP AND GRANT OF LICENSE:
10 | Licensor retains exclusive ownership of any copy of the Software (as defined below) licensed under this Agreement and hereby grants to Licensee a personal, non-exclusive, non-transferable license to use the Software for noncommercial research purposes, without the right to sublicense, pursuant to the terms and conditions of this Agreement. As used in this Agreement, the term "Software" means (i) the actual copy of all or any portion of code for program routines made accessible to Licensee by Licensor pursuant to this Agreement, inclusive of backups, updates, and/or merged copies permitted hereunder or subsequently supplied by Licensor, including all or any file structures, programming instructions, user interfaces and screen formats and sequences as well as any and all documentation and instructions related to it, and (ii) all or any derivatives and/or modifications created or made by You to any of the items specified in (i).
11 |
12 | CONFIDENTIALITY: Licensee acknowledges that the Software is proprietary to Licensor, and as such, Licensee agrees to receive all such materials in confidence and use the Software only in accordance with the terms of this Agreement. Licensee agrees to use reasonable effort to protect the Software from unauthorized use, reproduction, distribution, or publication.
13 |
14 | COPYRIGHT: The Software is owned by Licensor and is protected by United States copyright laws and applicable international treaties and/or conventions.
15 |
16 | PERMITTED USES: The Software may be used for your own noncommercial internal research purposes. You understand and agree that Licensor is not obligated to implement any suggestions and/or feedback you might provide regarding the Software, but to the extent Licensor does so, you are not entitled to any compensation related thereto.
17 |
18 | DERIVATIVES: You may create derivatives of or make modifications to the Software, however, You agree that all and any such derivatives and modifications will be owned by Licensor and become a part of the Software licensed to You under this Agreement. You may only use such derivatives and modifications for your own noncommercial internal research purposes, and you may not otherwise use, distribute or copy such derivatives and modifications in violation of this Agreement.
19 |
20 | BACKUPS: If Licensee is an organization, it may make that number of copies of the Software necessary for internal noncommercial use at a single site within its organization provided that all information appearing in or on the original labels, including the copyright and trademark notices are copied onto the labels of the copies.
21 |
22 | USES NOT PERMITTED: You may not distribute, copy or use the Software except as explicitly permitted herein. Licensee has not been granted any trademark license as part of this Agreement and may not use the name or mark "Magic Leap" or any renditions thereof without the prior written permission of Licensor.
23 |
24 | You may not sell, rent, lease, sublicense, lend, time-share or transfer, in whole or in part, or provide third parties access to prior or present versions (or any parts thereof) of the Software.
25 |
26 | ASSIGNMENT: You may not assign this Agreement or your rights hereunder without the prior written consent of Licensor. Any attempted assignment without such consent shall be null and void.
27 |
28 | TERM: The term of the license granted by this Agreement is from Licensee's acceptance of this Agreement by downloading the Software or by using the Software until terminated as provided below.
29 |
30 | The Agreement automatically terminates without notice if you fail to comply with any provision of this Agreement. Licensee may terminate this Agreement by ceasing using the Software. Upon any termination of this Agreement, Licensee will delete any and all copies of the Software. You agree that all provisions which operate to protect the proprietary rights of Licensor shall remain in force should breach occur and that the obligation of confidentiality described in this Agreement is binding in perpetuity and, as such, survives the term of the Agreement.
31 |
32 | FEE: Provided Licensee abides completely by the terms and conditions of this Agreement, there is no fee due to Licensor for Licensee's use of the Software in accordance with this Agreement.
33 |
34 | DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON-INFRINGEMENT. LICENSEE BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND RELATED MATERIALS.
35 |
36 | SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is provided as part of this Agreement.
37 |
38 | EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent permitted under applicable law, Licensor shall not be liable for direct, indirect, special, incidental, or consequential damages or lost profits related to Licensee's use of and/or inability to use the Software, even if Licensor is advised of the possibility of such damage.
39 |
40 | EXPORT REGULATION: Licensee agrees to comply with any and all applicable U.S. export control laws, regulations, and/or other laws related to embargoes and sanction programs administered by the Office of Foreign Assets Control.
41 |
42 | SEVERABILITY: If any provision(s) of this Agreement shall be held to be invalid, illegal, or unenforceable by a court or other tribunal of competent jurisdiction, the validity, legality and enforceability of the remaining provisions shall not in any way be affected or impaired thereby.
43 |
44 | NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right or remedy under this Agreement shall be construed as a waiver of any future or other exercise of such right or remedy by Licensor.
45 |
46 | GOVERNING LAW: This Agreement shall be construed and enforced in accordance with the laws of the State of Florida without reference to conflict of laws principles. You consent to the personal jurisdiction of the courts of this County and waive their rights to venue outside of Broward County, Florida.
47 |
48 | ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and entire agreement between Licensee and Licensor as to the matter set forth herein and supersedes any previous agreements, understandings, and arrangements between the parties relating hereto.
49 |
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 | import h5py
4 |
5 | cv2_greyscale = lambda x: cv2.cvtColor(x, cv2.COLOR_BGR2GRAY)
6 |
7 | cv2_scale = lambda x: cv2.resize(x, dsize=(32, 32),
8 | interpolation=cv2.INTER_LINEAR)
9 | # reshape image
10 | np_reshape = lambda x: np.reshape(x, (32, 32, 1))
11 |
12 |
13 | def str2bool(v):
14 | if v.lower() in ('yes', 'true', 't', 'y', '1'):
15 | return True
16 | elif v.lower() in ('no', 'false', 'f', 'n', '0'):
17 | return False
18 |
19 |
20 | def save_h5(dict_to_save, filename):
21 | """Saves dictionary to hdf5 file"""
22 |
23 | with h5py.File(filename, 'w') as f:
24 | for key in dict_to_save:
25 | f.create_dataset(key, data=dict_to_save[key])
26 |
--------------------------------------------------------------------------------