Spaces:
No application file
No application file
Commit
·
d8b9fcc
1
Parent(s):
a46fe5e
tam
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- app.py +0 -28
- assets/demo.gif +0 -3
- assets/gui.jpg +0 -3
- assets/logo.webp +0 -0
- test_app_cli.py +0 -114
- third_party/ALIKE/LICENSE +0 -29
- third_party/ALIKE/README.md +0 -131
- third_party/ALIKE/alike.py +0 -198
- third_party/ALIKE/alnet.py +0 -194
- third_party/ALIKE/assets/ALIKE_code.zip +0 -3
- third_party/ALIKE/assets/alike.png +0 -3
- third_party/ALIKE/assets/kitti.gif +0 -3
- third_party/ALIKE/assets/kitti/000100.png +0 -3
- third_party/ALIKE/assets/kitti/000101.png +0 -3
- third_party/ALIKE/assets/kitti/000102.png +0 -3
- third_party/ALIKE/assets/kitti/000103.png +0 -3
- third_party/ALIKE/assets/kitti/000104.png +0 -3
- third_party/ALIKE/assets/kitti/000105.png +0 -3
- third_party/ALIKE/assets/kitti/000106.png +0 -3
- third_party/ALIKE/assets/kitti/000107.png +0 -3
- third_party/ALIKE/assets/kitti/000108.png +0 -3
- third_party/ALIKE/assets/kitti/000109.png +0 -3
- third_party/ALIKE/assets/kitti/000110.png +0 -3
- third_party/ALIKE/assets/kitti/000111.png +0 -3
- third_party/ALIKE/assets/kitti/000112.png +0 -3
- third_party/ALIKE/assets/kitti/000113.png +0 -3
- third_party/ALIKE/assets/kitti/000114.png +0 -3
- third_party/ALIKE/assets/kitti/000115.png +0 -3
- third_party/ALIKE/assets/kitti/000116.png +0 -3
- third_party/ALIKE/assets/kitti/000117.png +0 -3
- third_party/ALIKE/assets/kitti/000118.png +0 -3
- third_party/ALIKE/assets/kitti/000119.png +0 -3
- third_party/ALIKE/assets/tum.gif +0 -3
- third_party/ALIKE/assets/tum/1311868169.163498.png +0 -3
- third_party/ALIKE/assets/tum/1311868169.263274.png +0 -3
- third_party/ALIKE/assets/tum/1311868169.363470.png +0 -3
- third_party/ALIKE/assets/tum/1311868169.463229.png +0 -3
- third_party/ALIKE/assets/tum/1311868169.563501.png +0 -3
- third_party/ALIKE/assets/tum/1311868169.663240.png +0 -3
- third_party/ALIKE/assets/tum/1311868169.763417.png +0 -3
- third_party/ALIKE/assets/tum/1311868169.863396.png +0 -3
- third_party/ALIKE/assets/tum/1311868169.963415.png +0 -3
- third_party/ALIKE/assets/tum/1311868170.063469.png +0 -3
- third_party/ALIKE/assets/tum/1311868170.163416.png +0 -3
- third_party/ALIKE/assets/tum/1311868170.263521.png +0 -3
- third_party/ALIKE/assets/tum/1311868170.363400.png +0 -3
- third_party/ALIKE/assets/tum/1311868170.463383.png +0 -3
- third_party/ALIKE/assets/tum/1311868170.563345.png +0 -3
- third_party/ALIKE/assets/tum/1311868170.663430.png +0 -3
- third_party/ALIKE/assets/tum/1311868170.763453.png +0 -3
app.py
DELETED
@@ -1,28 +0,0 @@
|
|
1 |
-
import argparse
|
2 |
-
from pathlib import Path
|
3 |
-
from common.app_class import ImageMatchingApp
|
4 |
-
|
5 |
-
if __name__ == "__main__":
|
6 |
-
parser = argparse.ArgumentParser()
|
7 |
-
parser.add_argument(
|
8 |
-
"--server_name",
|
9 |
-
type=str,
|
10 |
-
default="0.0.0.0",
|
11 |
-
help="server name",
|
12 |
-
)
|
13 |
-
parser.add_argument(
|
14 |
-
"--server_port",
|
15 |
-
type=int,
|
16 |
-
default=7860,
|
17 |
-
help="server port",
|
18 |
-
)
|
19 |
-
parser.add_argument(
|
20 |
-
"--config",
|
21 |
-
type=str,
|
22 |
-
default=Path(__file__).parent / "common/config.yaml",
|
23 |
-
help="config file",
|
24 |
-
)
|
25 |
-
args = parser.parse_args()
|
26 |
-
ImageMatchingApp(
|
27 |
-
args.server_name, args.server_port, config=args.config
|
28 |
-
).run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/demo.gif
DELETED
Git LFS Details
|
assets/gui.jpg
DELETED
Git LFS Details
|
assets/logo.webp
DELETED
Binary file (404 kB)
|
|
test_app_cli.py
DELETED
@@ -1,114 +0,0 @@
|
|
1 |
-
import cv2
|
2 |
-
import warnings
|
3 |
-
import numpy as np
|
4 |
-
from pathlib import Path
|
5 |
-
from hloc import logger
|
6 |
-
from common.utils import (
|
7 |
-
get_matcher_zoo,
|
8 |
-
load_config,
|
9 |
-
DEVICE,
|
10 |
-
ROOT,
|
11 |
-
)
|
12 |
-
from common.api import ImageMatchingAPI
|
13 |
-
|
14 |
-
|
15 |
-
def test_all(config: dict = None):
|
16 |
-
img_path1 = ROOT / "datasets/sacre_coeur/mapping/02928139_3448003521.jpg"
|
17 |
-
img_path2 = ROOT / "datasets/sacre_coeur/mapping/17295357_9106075285.jpg"
|
18 |
-
image0 = cv2.imread(str(img_path1))[:, :, ::-1] # RGB
|
19 |
-
image1 = cv2.imread(str(img_path2))[:, :, ::-1] # RGB
|
20 |
-
|
21 |
-
matcher_zoo_restored = get_matcher_zoo(config["matcher_zoo"])
|
22 |
-
for k, v in matcher_zoo_restored.items():
|
23 |
-
if image0 is None or image1 is None:
|
24 |
-
logger.error("Error: No images found! Please upload two images.")
|
25 |
-
enable = config["matcher_zoo"][k].get("enable", True)
|
26 |
-
skip_ci = config["matcher_zoo"][k].get("skip_ci", False)
|
27 |
-
if enable and not skip_ci:
|
28 |
-
logger.info(f"Testing {k} ...")
|
29 |
-
api = ImageMatchingAPI(conf=v, device=DEVICE)
|
30 |
-
api(image0, image1)
|
31 |
-
log_path = ROOT / "experiments" / "all"
|
32 |
-
log_path.mkdir(exist_ok=True, parents=True)
|
33 |
-
api.visualize(log_path=log_path)
|
34 |
-
else:
|
35 |
-
logger.info(f"Skipping {k} ...")
|
36 |
-
return 0
|
37 |
-
|
38 |
-
|
39 |
-
def test_one():
|
40 |
-
img_path1 = ROOT / "datasets/sacre_coeur/mapping/02928139_3448003521.jpg"
|
41 |
-
img_path2 = ROOT / "datasets/sacre_coeur/mapping/17295357_9106075285.jpg"
|
42 |
-
image0 = cv2.imread(str(img_path1))[:, :, ::-1] # RGB
|
43 |
-
image1 = cv2.imread(str(img_path2))[:, :, ::-1] # RGB
|
44 |
-
# sparse
|
45 |
-
conf = {
|
46 |
-
"feature": {
|
47 |
-
"output": "feats-superpoint-n4096-rmax1600",
|
48 |
-
"model": {
|
49 |
-
"name": "superpoint",
|
50 |
-
"nms_radius": 3,
|
51 |
-
"max_keypoints": 4096,
|
52 |
-
"keypoint_threshold": 0.005,
|
53 |
-
},
|
54 |
-
"preprocessing": {
|
55 |
-
"grayscale": True,
|
56 |
-
"force_resize": True,
|
57 |
-
"resize_max": 1600,
|
58 |
-
"width": 640,
|
59 |
-
"height": 480,
|
60 |
-
"dfactor": 8,
|
61 |
-
},
|
62 |
-
},
|
63 |
-
"matcher": {
|
64 |
-
"output": "matches-NN-mutual",
|
65 |
-
"model": {
|
66 |
-
"name": "nearest_neighbor",
|
67 |
-
"do_mutual_check": True,
|
68 |
-
"match_threshold": 0.2,
|
69 |
-
},
|
70 |
-
},
|
71 |
-
"dense": False,
|
72 |
-
}
|
73 |
-
api = ImageMatchingAPI(conf=conf, device=DEVICE)
|
74 |
-
api(image0, image1)
|
75 |
-
log_path = ROOT / "experiments" / "one"
|
76 |
-
log_path.mkdir(exist_ok=True, parents=True)
|
77 |
-
api.visualize(log_path=log_path)
|
78 |
-
|
79 |
-
# dense
|
80 |
-
conf = {
|
81 |
-
"matcher": {
|
82 |
-
"output": "matches-loftr",
|
83 |
-
"model": {
|
84 |
-
"name": "loftr",
|
85 |
-
"weights": "outdoor",
|
86 |
-
"max_keypoints": 2000,
|
87 |
-
"match_threshold": 0.2,
|
88 |
-
},
|
89 |
-
"preprocessing": {
|
90 |
-
"grayscale": True,
|
91 |
-
"resize_max": 1024,
|
92 |
-
"dfactor": 8,
|
93 |
-
"width": 640,
|
94 |
-
"height": 480,
|
95 |
-
"force_resize": True,
|
96 |
-
},
|
97 |
-
"max_error": 1,
|
98 |
-
"cell_size": 1,
|
99 |
-
},
|
100 |
-
"dense": True,
|
101 |
-
}
|
102 |
-
|
103 |
-
api = ImageMatchingAPI(conf=conf, device=DEVICE)
|
104 |
-
api(image0, image1)
|
105 |
-
log_path = ROOT / "experiments" / "one"
|
106 |
-
log_path.mkdir(exist_ok=True, parents=True)
|
107 |
-
api.visualize(log_path=log_path)
|
108 |
-
return 0
|
109 |
-
|
110 |
-
|
111 |
-
if __name__ == "__main__":
|
112 |
-
config = load_config(ROOT / "common/config.yaml")
|
113 |
-
test_one()
|
114 |
-
test_all(config)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
third_party/ALIKE/LICENSE
DELETED
@@ -1,29 +0,0 @@
|
|
1 |
-
BSD 3-Clause License
|
2 |
-
|
3 |
-
Copyright (c) 2022, Zhao Xiaoming
|
4 |
-
All rights reserved.
|
5 |
-
|
6 |
-
Redistribution and use in source and binary forms, with or without
|
7 |
-
modification, are permitted provided that the following conditions are met:
|
8 |
-
|
9 |
-
1. Redistributions of source code must retain the above copyright notice, this
|
10 |
-
list of conditions and the following disclaimer.
|
11 |
-
|
12 |
-
2. Redistributions in binary form must reproduce the above copyright notice,
|
13 |
-
this list of conditions and the following disclaimer in the documentation
|
14 |
-
and/or other materials provided with the distribution.
|
15 |
-
|
16 |
-
3. Neither the name of the copyright holder nor the names of its
|
17 |
-
contributors may be used to endorse or promote products derived from
|
18 |
-
this software without specific prior written permission.
|
19 |
-
|
20 |
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21 |
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22 |
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23 |
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
24 |
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
25 |
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26 |
-
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
27 |
-
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
28 |
-
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29 |
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
third_party/ALIKE/README.md
DELETED
@@ -1,131 +0,0 @@
|
|
1 |
-
# News
|
2 |
-
|
3 |
-
- The [ALIKED](https://github.com/Shiaoming/ALIKED) is released.
|
4 |
-
- The [ALIKE training code](https://github.com/Shiaoming/ALIKE/raw/main/assets/ALIKE_code.zip) is released.
|
5 |
-
|
6 |
-
# ALIKE: Accurate and Lightweight Keypoint Detection and Descriptor Extraction
|
7 |
-
|
8 |
-
ALIKE applies a differentiable keypoint detection module to detect accurate sub-pixel keypoints. The network can run at 95 frames per second for 640 x 480 images on NVIDIA Titan X (Pascal) GPU and achieve equivalent performance with the state-of-the-arts. ALIKE benefits real-time applications in resource-limited platforms/devices. Technical details are described in [this paper](https://arxiv.org/pdf/2112.02906.pdf).
|
9 |
-
|
10 |
-
> ```
|
11 |
-
> Xiaoming Zhao, Xingming Wu, Jinyu Miao, Weihai Chen, Peter C. Y. Chen, Zhengguo Li, "ALIKE: Accurate and Lightweight Keypoint
|
12 |
-
> Detection and Descriptor Extraction," IEEE Transactions on Multimedia, 2022.
|
13 |
-
> ```
|
14 |
-
|
15 |
-

|
16 |
-
|
17 |
-
|
18 |
-
If you use ALIKE in an academic work, please cite:
|
19 |
-
|
20 |
-
```
|
21 |
-
@article{Zhao2023ALIKED,
|
22 |
-
title = {ALIKED: A Lighter Keypoint and Descriptor Extraction Network via Deformable Transformation},
|
23 |
-
url = {https://arxiv.org/pdf/2304.03608.pdf},
|
24 |
-
doi = {10.1109/TIM.2023.3271000},
|
25 |
-
journal = {IEEE Transactions on Instrumentation & Measurement},
|
26 |
-
author = {Zhao, Xiaoming and Wu, Xingming and Chen, Weihai and Chen, Peter C. Y. and Xu, Qingsong and Li, Zhengguo},
|
27 |
-
year = {2023},
|
28 |
-
volume = {72},
|
29 |
-
pages = {1-16},
|
30 |
-
}
|
31 |
-
|
32 |
-
@article{Zhao2022ALIKE,
|
33 |
-
title = {ALIKE: Accurate and Lightweight Keypoint Detection and Descriptor Extraction},
|
34 |
-
url = {http://arxiv.org/abs/2112.02906},
|
35 |
-
doi = {10.1109/TMM.2022.3155927},
|
36 |
-
journal = {IEEE Transactions on Multimedia},
|
37 |
-
author = {Zhao, Xiaoming and Wu, Xingming and Miao, Jinyu and Chen, Weihai and Chen, Peter C. Y. and Li, Zhengguo},
|
38 |
-
month = march,
|
39 |
-
year = {2022},
|
40 |
-
}
|
41 |
-
```
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
## 1. Prerequisites
|
46 |
-
|
47 |
-
The required packages are listed in the `requirements.txt` :
|
48 |
-
|
49 |
-
```shell
|
50 |
-
pip install -r requirements.txt
|
51 |
-
```
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
## 2. Models
|
56 |
-
|
57 |
-
The off-the-shelf weights of four variant ALIKE models are provided in `models/` .
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
## 3. Run demo
|
62 |
-
|
63 |
-
```shell
|
64 |
-
$ python demo.py -h
|
65 |
-
usage: demo.py [-h] [--model {alike-t,alike-s,alike-n,alike-l}]
|
66 |
-
[--device DEVICE] [--top_k TOP_K] [--scores_th SCORES_TH]
|
67 |
-
[--n_limit N_LIMIT] [--no_display] [--no_sub_pixel]
|
68 |
-
input
|
69 |
-
|
70 |
-
ALike Demo.
|
71 |
-
|
72 |
-
positional arguments:
|
73 |
-
input Image directory or movie file or "camera0" (for
|
74 |
-
webcam0).
|
75 |
-
|
76 |
-
optional arguments:
|
77 |
-
-h, --help show this help message and exit
|
78 |
-
--model {alike-t,alike-s,alike-n,alike-l}
|
79 |
-
The model configuration
|
80 |
-
--device DEVICE Running device (default: cuda).
|
81 |
-
--top_k TOP_K Detect top K keypoints. -1 for threshold based mode,
|
82 |
-
>0 for top K mode. (default: -1)
|
83 |
-
--scores_th SCORES_TH
|
84 |
-
Detector score threshold (default: 0.2).
|
85 |
-
--n_limit N_LIMIT Maximum number of keypoints to be detected (default:
|
86 |
-
5000).
|
87 |
-
--no_display Do not display images to screen. Useful if running
|
88 |
-
remotely (default: False).
|
89 |
-
--no_sub_pixel Do not detect sub-pixel keypoints (default: False).
|
90 |
-
```
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
## 4. Examples
|
95 |
-
|
96 |
-
### KITTI example
|
97 |
-
```shell
|
98 |
-
python demo.py assets/kitti
|
99 |
-
```
|
100 |
-

|
101 |
-
|
102 |
-
### TUM example
|
103 |
-
```shell
|
104 |
-
python demo.py assets/tum
|
105 |
-
```
|
106 |
-

|
107 |
-
|
108 |
-
## 5. Efficiency and performance
|
109 |
-
|
110 |
-
| Models | Parameters | GFLOPs(640x480) | MHA@3 on Hpatches | mAA(10°) on [IMW2020-test](https://www.cs.ubc.ca/research/image-matching-challenge/2021/leaderboard) (Stereo) |
|
111 |
-
|:---:|:---:|:---:|:-----------------:|:-------------------------------------------------------------------------------------------------------------:|
|
112 |
-
| D2-Net(MS) | 7653KB | 889.40 | 38.33% | 12.27% |
|
113 |
-
| LF-Net(MS) | 2642KB | 24.37 | 57.78% | 23.44% |
|
114 |
-
| SuperPoint | 1301KB | 26.11 | 70.19% | 28.97% |
|
115 |
-
| R2D2(MS) | 484KB | 464.55 | 71.48% | 39.02% |
|
116 |
-
| ASLFeat(MS) | 823KB | 77.58 | 73.52% | 33.65% |
|
117 |
-
| DISK | 1092KB | 98.97 | 70.56% | 51.22% |
|
118 |
-
| ALike-N | 318KB | 7.909 | 75.74% | 47.18% |
|
119 |
-
| ALike-L | 653KB | 19.685 | 76.85% | 49.58% |
|
120 |
-
|
121 |
-
### Evaluation on Hpatches
|
122 |
-
|
123 |
-
- Download [hpatches-sequences-release](https://hpatches.github.io/) and put it into `hseq/hpatches-sequences-release`.
|
124 |
-
- Remove the unreliable sequences as D2-Net.
|
125 |
-
- Run the following command to evaluate the performance:
|
126 |
-
```shell
|
127 |
-
python hseq/eval.py
|
128 |
-
```
|
129 |
-
|
130 |
-
|
131 |
-
For more details, please refer to the [paper](https://arxiv.org/abs/2112.02906).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
third_party/ALIKE/alike.py
DELETED
@@ -1,198 +0,0 @@
|
|
1 |
-
import logging
|
2 |
-
import os
|
3 |
-
import cv2
|
4 |
-
import torch
|
5 |
-
from copy import deepcopy
|
6 |
-
import torch.nn.functional as F
|
7 |
-
from torchvision.transforms import ToTensor
|
8 |
-
import math
|
9 |
-
|
10 |
-
from alnet import ALNet
|
11 |
-
from soft_detect import DKD
|
12 |
-
import time
|
13 |
-
|
14 |
-
configs = {
|
15 |
-
"alike-t": {
|
16 |
-
"c1": 8,
|
17 |
-
"c2": 16,
|
18 |
-
"c3": 32,
|
19 |
-
"c4": 64,
|
20 |
-
"dim": 64,
|
21 |
-
"single_head": True,
|
22 |
-
"radius": 2,
|
23 |
-
"model_path": os.path.join(os.path.split(__file__)[0], "models", "alike-t.pth"),
|
24 |
-
},
|
25 |
-
"alike-s": {
|
26 |
-
"c1": 8,
|
27 |
-
"c2": 16,
|
28 |
-
"c3": 48,
|
29 |
-
"c4": 96,
|
30 |
-
"dim": 96,
|
31 |
-
"single_head": True,
|
32 |
-
"radius": 2,
|
33 |
-
"model_path": os.path.join(os.path.split(__file__)[0], "models", "alike-s.pth"),
|
34 |
-
},
|
35 |
-
"alike-n": {
|
36 |
-
"c1": 16,
|
37 |
-
"c2": 32,
|
38 |
-
"c3": 64,
|
39 |
-
"c4": 128,
|
40 |
-
"dim": 128,
|
41 |
-
"single_head": True,
|
42 |
-
"radius": 2,
|
43 |
-
"model_path": os.path.join(os.path.split(__file__)[0], "models", "alike-n.pth"),
|
44 |
-
},
|
45 |
-
"alike-l": {
|
46 |
-
"c1": 32,
|
47 |
-
"c2": 64,
|
48 |
-
"c3": 128,
|
49 |
-
"c4": 128,
|
50 |
-
"dim": 128,
|
51 |
-
"single_head": False,
|
52 |
-
"radius": 2,
|
53 |
-
"model_path": os.path.join(os.path.split(__file__)[0], "models", "alike-l.pth"),
|
54 |
-
},
|
55 |
-
}
|
56 |
-
|
57 |
-
|
58 |
-
class ALike(ALNet):
|
59 |
-
def __init__(
|
60 |
-
self,
|
61 |
-
# ================================== feature encoder
|
62 |
-
c1: int = 32,
|
63 |
-
c2: int = 64,
|
64 |
-
c3: int = 128,
|
65 |
-
c4: int = 128,
|
66 |
-
dim: int = 128,
|
67 |
-
single_head: bool = False,
|
68 |
-
# ================================== detect parameters
|
69 |
-
radius: int = 2,
|
70 |
-
top_k: int = 500,
|
71 |
-
scores_th: float = 0.5,
|
72 |
-
n_limit: int = 5000,
|
73 |
-
device: str = "cpu",
|
74 |
-
model_path: str = "",
|
75 |
-
):
|
76 |
-
super().__init__(c1, c2, c3, c4, dim, single_head)
|
77 |
-
self.radius = radius
|
78 |
-
self.top_k = top_k
|
79 |
-
self.n_limit = n_limit
|
80 |
-
self.scores_th = scores_th
|
81 |
-
self.dkd = DKD(
|
82 |
-
radius=self.radius,
|
83 |
-
top_k=self.top_k,
|
84 |
-
scores_th=self.scores_th,
|
85 |
-
n_limit=self.n_limit,
|
86 |
-
)
|
87 |
-
self.device = device
|
88 |
-
|
89 |
-
if model_path != "":
|
90 |
-
state_dict = torch.load(model_path, self.device)
|
91 |
-
self.load_state_dict(state_dict)
|
92 |
-
self.to(self.device)
|
93 |
-
self.eval()
|
94 |
-
logging.info(f"Loaded model parameters from {model_path}")
|
95 |
-
logging.info(
|
96 |
-
f"Number of model parameters: {sum(p.numel() for p in self.parameters() if p.requires_grad) / 1e3}KB"
|
97 |
-
)
|
98 |
-
|
99 |
-
def extract_dense_map(self, image, ret_dict=False):
|
100 |
-
# ====================================================
|
101 |
-
# check image size, should be integer multiples of 2^5
|
102 |
-
# if it is not a integer multiples of 2^5, padding zeros
|
103 |
-
device = image.device
|
104 |
-
b, c, h, w = image.shape
|
105 |
-
h_ = math.ceil(h / 32) * 32 if h % 32 != 0 else h
|
106 |
-
w_ = math.ceil(w / 32) * 32 if w % 32 != 0 else w
|
107 |
-
if h_ != h:
|
108 |
-
h_padding = torch.zeros(b, c, h_ - h, w, device=device)
|
109 |
-
image = torch.cat([image, h_padding], dim=2)
|
110 |
-
if w_ != w:
|
111 |
-
w_padding = torch.zeros(b, c, h_, w_ - w, device=device)
|
112 |
-
image = torch.cat([image, w_padding], dim=3)
|
113 |
-
# ====================================================
|
114 |
-
|
115 |
-
scores_map, descriptor_map = super().forward(image)
|
116 |
-
|
117 |
-
# ====================================================
|
118 |
-
if h_ != h or w_ != w:
|
119 |
-
descriptor_map = descriptor_map[:, :, :h, :w]
|
120 |
-
scores_map = scores_map[:, :, :h, :w] # Bx1xHxW
|
121 |
-
# ====================================================
|
122 |
-
|
123 |
-
# BxCxHxW
|
124 |
-
descriptor_map = torch.nn.functional.normalize(descriptor_map, p=2, dim=1)
|
125 |
-
|
126 |
-
if ret_dict:
|
127 |
-
return {
|
128 |
-
"descriptor_map": descriptor_map,
|
129 |
-
"scores_map": scores_map,
|
130 |
-
}
|
131 |
-
else:
|
132 |
-
return descriptor_map, scores_map
|
133 |
-
|
134 |
-
def forward(self, img, image_size_max=99999, sort=False, sub_pixel=False):
|
135 |
-
"""
|
136 |
-
:param img: np.array HxWx3, RGB
|
137 |
-
:param image_size_max: maximum image size, otherwise, the image will be resized
|
138 |
-
:param sort: sort keypoints by scores
|
139 |
-
:param sub_pixel: whether to use sub-pixel accuracy
|
140 |
-
:return: a dictionary with 'keypoints', 'descriptors', 'scores', and 'time'
|
141 |
-
"""
|
142 |
-
H, W, three = img.shape
|
143 |
-
assert three == 3, "input image shape should be [HxWx3]"
|
144 |
-
|
145 |
-
# ==================== image size constraint
|
146 |
-
image = deepcopy(img)
|
147 |
-
max_hw = max(H, W)
|
148 |
-
if max_hw > image_size_max:
|
149 |
-
ratio = float(image_size_max / max_hw)
|
150 |
-
image = cv2.resize(image, dsize=None, fx=ratio, fy=ratio)
|
151 |
-
|
152 |
-
# ==================== convert image to tensor
|
153 |
-
image = (
|
154 |
-
torch.from_numpy(image)
|
155 |
-
.to(self.device)
|
156 |
-
.to(torch.float32)
|
157 |
-
.permute(2, 0, 1)[None]
|
158 |
-
/ 255.0
|
159 |
-
)
|
160 |
-
|
161 |
-
# ==================== extract keypoints
|
162 |
-
start = time.time()
|
163 |
-
|
164 |
-
with torch.no_grad():
|
165 |
-
descriptor_map, scores_map = self.extract_dense_map(image)
|
166 |
-
keypoints, descriptors, scores, _ = self.dkd(
|
167 |
-
scores_map, descriptor_map, sub_pixel=sub_pixel
|
168 |
-
)
|
169 |
-
keypoints, descriptors, scores = keypoints[0], descriptors[0], scores[0]
|
170 |
-
keypoints = (keypoints + 1) / 2 * keypoints.new_tensor([[W - 1, H - 1]])
|
171 |
-
|
172 |
-
if sort:
|
173 |
-
indices = torch.argsort(scores, descending=True)
|
174 |
-
keypoints = keypoints[indices]
|
175 |
-
descriptors = descriptors[indices]
|
176 |
-
scores = scores[indices]
|
177 |
-
|
178 |
-
end = time.time()
|
179 |
-
|
180 |
-
return {
|
181 |
-
"keypoints": keypoints.cpu().numpy(),
|
182 |
-
"descriptors": descriptors.cpu().numpy(),
|
183 |
-
"scores": scores.cpu().numpy(),
|
184 |
-
"scores_map": scores_map.cpu().numpy(),
|
185 |
-
"time": end - start,
|
186 |
-
}
|
187 |
-
|
188 |
-
|
189 |
-
if __name__ == "__main__":
|
190 |
-
import numpy as np
|
191 |
-
from thop import profile
|
192 |
-
|
193 |
-
net = ALike(c1=32, c2=64, c3=128, c4=128, dim=128, single_head=False)
|
194 |
-
|
195 |
-
image = np.random.random((640, 480, 3)).astype(np.float32)
|
196 |
-
flops, params = profile(net, inputs=(image, 9999, False), verbose=False)
|
197 |
-
print("{:<30} {:<8} GFLops".format("Computational complexity: ", flops / 1e9))
|
198 |
-
print("{:<30} {:<8} KB".format("Number of parameters: ", params / 1e3))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
third_party/ALIKE/alnet.py
DELETED
@@ -1,194 +0,0 @@
|
|
1 |
-
import torch
|
2 |
-
from torch import nn
|
3 |
-
from torchvision.models import resnet
|
4 |
-
from typing import Optional, Callable
|
5 |
-
|
6 |
-
|
7 |
-
class ConvBlock(nn.Module):
|
8 |
-
def __init__(
|
9 |
-
self,
|
10 |
-
in_channels,
|
11 |
-
out_channels,
|
12 |
-
gate: Optional[Callable[..., nn.Module]] = None,
|
13 |
-
norm_layer: Optional[Callable[..., nn.Module]] = None,
|
14 |
-
):
|
15 |
-
super().__init__()
|
16 |
-
if gate is None:
|
17 |
-
self.gate = nn.ReLU(inplace=True)
|
18 |
-
else:
|
19 |
-
self.gate = gate
|
20 |
-
if norm_layer is None:
|
21 |
-
norm_layer = nn.BatchNorm2d
|
22 |
-
self.conv1 = resnet.conv3x3(in_channels, out_channels)
|
23 |
-
self.bn1 = norm_layer(out_channels)
|
24 |
-
self.conv2 = resnet.conv3x3(out_channels, out_channels)
|
25 |
-
self.bn2 = norm_layer(out_channels)
|
26 |
-
|
27 |
-
def forward(self, x):
|
28 |
-
x = self.gate(self.bn1(self.conv1(x))) # B x in_channels x H x W
|
29 |
-
x = self.gate(self.bn2(self.conv2(x))) # B x out_channels x H x W
|
30 |
-
return x
|
31 |
-
|
32 |
-
|
33 |
-
# copied from torchvision\models\resnet.py#27->BasicBlock
|
34 |
-
class ResBlock(nn.Module):
|
35 |
-
expansion: int = 1
|
36 |
-
|
37 |
-
def __init__(
|
38 |
-
self,
|
39 |
-
inplanes: int,
|
40 |
-
planes: int,
|
41 |
-
stride: int = 1,
|
42 |
-
downsample: Optional[nn.Module] = None,
|
43 |
-
groups: int = 1,
|
44 |
-
base_width: int = 64,
|
45 |
-
dilation: int = 1,
|
46 |
-
gate: Optional[Callable[..., nn.Module]] = None,
|
47 |
-
norm_layer: Optional[Callable[..., nn.Module]] = None,
|
48 |
-
) -> None:
|
49 |
-
super(ResBlock, self).__init__()
|
50 |
-
if gate is None:
|
51 |
-
self.gate = nn.ReLU(inplace=True)
|
52 |
-
else:
|
53 |
-
self.gate = gate
|
54 |
-
if norm_layer is None:
|
55 |
-
norm_layer = nn.BatchNorm2d
|
56 |
-
if groups != 1 or base_width != 64:
|
57 |
-
raise ValueError("ResBlock only supports groups=1 and base_width=64")
|
58 |
-
if dilation > 1:
|
59 |
-
raise NotImplementedError("Dilation > 1 not supported in ResBlock")
|
60 |
-
# Both self.conv1 and self.downsample layers downsample the input when stride != 1
|
61 |
-
self.conv1 = resnet.conv3x3(inplanes, planes, stride)
|
62 |
-
self.bn1 = norm_layer(planes)
|
63 |
-
self.conv2 = resnet.conv3x3(planes, planes)
|
64 |
-
self.bn2 = norm_layer(planes)
|
65 |
-
self.downsample = downsample
|
66 |
-
self.stride = stride
|
67 |
-
|
68 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
69 |
-
identity = x
|
70 |
-
|
71 |
-
out = self.conv1(x)
|
72 |
-
out = self.bn1(out)
|
73 |
-
out = self.gate(out)
|
74 |
-
|
75 |
-
out = self.conv2(out)
|
76 |
-
out = self.bn2(out)
|
77 |
-
|
78 |
-
if self.downsample is not None:
|
79 |
-
identity = self.downsample(x)
|
80 |
-
|
81 |
-
out += identity
|
82 |
-
out = self.gate(out)
|
83 |
-
|
84 |
-
return out
|
85 |
-
|
86 |
-
|
87 |
-
class ALNet(nn.Module):
|
88 |
-
def __init__(
|
89 |
-
self,
|
90 |
-
c1: int = 32,
|
91 |
-
c2: int = 64,
|
92 |
-
c3: int = 128,
|
93 |
-
c4: int = 128,
|
94 |
-
dim: int = 128,
|
95 |
-
single_head: bool = True,
|
96 |
-
):
|
97 |
-
super().__init__()
|
98 |
-
|
99 |
-
self.gate = nn.ReLU(inplace=True)
|
100 |
-
|
101 |
-
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
|
102 |
-
self.pool4 = nn.MaxPool2d(kernel_size=4, stride=4)
|
103 |
-
|
104 |
-
self.block1 = ConvBlock(3, c1, self.gate, nn.BatchNorm2d)
|
105 |
-
|
106 |
-
self.block2 = ResBlock(
|
107 |
-
inplanes=c1,
|
108 |
-
planes=c2,
|
109 |
-
stride=1,
|
110 |
-
downsample=nn.Conv2d(c1, c2, 1),
|
111 |
-
gate=self.gate,
|
112 |
-
norm_layer=nn.BatchNorm2d,
|
113 |
-
)
|
114 |
-
self.block3 = ResBlock(
|
115 |
-
inplanes=c2,
|
116 |
-
planes=c3,
|
117 |
-
stride=1,
|
118 |
-
downsample=nn.Conv2d(c2, c3, 1),
|
119 |
-
gate=self.gate,
|
120 |
-
norm_layer=nn.BatchNorm2d,
|
121 |
-
)
|
122 |
-
self.block4 = ResBlock(
|
123 |
-
inplanes=c3,
|
124 |
-
planes=c4,
|
125 |
-
stride=1,
|
126 |
-
downsample=nn.Conv2d(c3, c4, 1),
|
127 |
-
gate=self.gate,
|
128 |
-
norm_layer=nn.BatchNorm2d,
|
129 |
-
)
|
130 |
-
|
131 |
-
# ================================== feature aggregation
|
132 |
-
self.conv1 = resnet.conv1x1(c1, dim // 4)
|
133 |
-
self.conv2 = resnet.conv1x1(c2, dim // 4)
|
134 |
-
self.conv3 = resnet.conv1x1(c3, dim // 4)
|
135 |
-
self.conv4 = resnet.conv1x1(dim, dim // 4)
|
136 |
-
self.upsample2 = nn.Upsample(
|
137 |
-
scale_factor=2, mode="bilinear", align_corners=True
|
138 |
-
)
|
139 |
-
self.upsample4 = nn.Upsample(
|
140 |
-
scale_factor=4, mode="bilinear", align_corners=True
|
141 |
-
)
|
142 |
-
self.upsample8 = nn.Upsample(
|
143 |
-
scale_factor=8, mode="bilinear", align_corners=True
|
144 |
-
)
|
145 |
-
self.upsample32 = nn.Upsample(
|
146 |
-
scale_factor=32, mode="bilinear", align_corners=True
|
147 |
-
)
|
148 |
-
|
149 |
-
# ================================== detector and descriptor head
|
150 |
-
self.single_head = single_head
|
151 |
-
if not self.single_head:
|
152 |
-
self.convhead1 = resnet.conv1x1(dim, dim)
|
153 |
-
self.convhead2 = resnet.conv1x1(dim, dim + 1)
|
154 |
-
|
155 |
-
def forward(self, image):
|
156 |
-
# ================================== feature encoder
|
157 |
-
x1 = self.block1(image) # B x c1 x H x W
|
158 |
-
x2 = self.pool2(x1)
|
159 |
-
x2 = self.block2(x2) # B x c2 x H/2 x W/2
|
160 |
-
x3 = self.pool4(x2)
|
161 |
-
x3 = self.block3(x3) # B x c3 x H/8 x W/8
|
162 |
-
x4 = self.pool4(x3)
|
163 |
-
x4 = self.block4(x4) # B x dim x H/32 x W/32
|
164 |
-
|
165 |
-
# ================================== feature aggregation
|
166 |
-
x1 = self.gate(self.conv1(x1)) # B x dim//4 x H x W
|
167 |
-
x2 = self.gate(self.conv2(x2)) # B x dim//4 x H//2 x W//2
|
168 |
-
x3 = self.gate(self.conv3(x3)) # B x dim//4 x H//8 x W//8
|
169 |
-
x4 = self.gate(self.conv4(x4)) # B x dim//4 x H//32 x W//32
|
170 |
-
x2_up = self.upsample2(x2) # B x dim//4 x H x W
|
171 |
-
x3_up = self.upsample8(x3) # B x dim//4 x H x W
|
172 |
-
x4_up = self.upsample32(x4) # B x dim//4 x H x W
|
173 |
-
x1234 = torch.cat([x1, x2_up, x3_up, x4_up], dim=1)
|
174 |
-
|
175 |
-
# ================================== detector and descriptor head
|
176 |
-
if not self.single_head:
|
177 |
-
x1234 = self.gate(self.convhead1(x1234))
|
178 |
-
x = self.convhead2(x1234) # B x dim+1 x H x W
|
179 |
-
|
180 |
-
descriptor_map = x[:, :-1, :, :]
|
181 |
-
scores_map = torch.sigmoid(x[:, -1, :, :]).unsqueeze(1)
|
182 |
-
|
183 |
-
return scores_map, descriptor_map
|
184 |
-
|
185 |
-
|
186 |
-
if __name__ == "__main__":
|
187 |
-
from thop import profile
|
188 |
-
|
189 |
-
net = ALNet(c1=16, c2=32, c3=64, c4=128, dim=128, single_head=True)
|
190 |
-
|
191 |
-
image = torch.randn(1, 3, 640, 480)
|
192 |
-
flops, params = profile(net, inputs=(image,), verbose=False)
|
193 |
-
print("{:<30} {:<8} GFLops".format("Computational complexity: ", flops / 1e9))
|
194 |
-
print("{:<30} {:<8} KB".format("Number of parameters: ", params / 1e3))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
third_party/ALIKE/assets/ALIKE_code.zip
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:891e8431c047e7aeed77c9e5f64ffeed262d92389d8ae6235dde0964a9048a08
|
3 |
-
size 62774
|
|
|
|
|
|
|
|
third_party/ALIKE/assets/alike.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti.gif
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000100.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000101.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000102.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000103.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000104.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000105.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000106.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000107.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000108.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000109.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000110.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000111.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000112.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000113.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000114.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000115.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000116.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000117.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000118.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/kitti/000119.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum.gif
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.163498.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.263274.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.363470.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.463229.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.563501.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.663240.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.763417.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.863396.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868169.963415.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868170.063469.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868170.163416.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868170.263521.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868170.363400.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868170.463383.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868170.563345.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868170.663430.png
DELETED
Git LFS Details
|
third_party/ALIKE/assets/tum/1311868170.763453.png
DELETED
Git LFS Details
|