Realcat commited on
Commit
45366e0
·
1 Parent(s): dfa5525

update: using imcui pkg

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. .gitignore +1 -0
  3. .pre-commit-config.yaml +88 -0
  4. Dockerfile +0 -27
  5. README.md +0 -155
  6. api/__init__.py +0 -0
  7. api/client.py +0 -225
  8. api/server.py +0 -499
  9. api/test/CMakeLists.txt +0 -16
  10. api/test/build_and_run.sh +0 -16
  11. api/test/client.cpp +0 -84
  12. api/test/helper.h +0 -410
  13. api/types.py +0 -16
  14. app.py +3 -5
  15. assets/gui.jpg +0 -3
  16. assets/logo.webp +0 -0
  17. build_docker.sh +0 -3
  18. {ui → config}/config.yaml +1 -1
  19. datasets/.gitignore +0 -0
  20. datasets/sacre_coeur/README.md +0 -3
  21. datasets/sacre_coeur/mapping/02928139_3448003521.jpg +0 -3
  22. datasets/sacre_coeur/mapping/03903474_1471484089.jpg +0 -3
  23. datasets/sacre_coeur/mapping/10265353_3838484249.jpg +0 -3
  24. datasets/sacre_coeur/mapping/17295357_9106075285.jpg +0 -3
  25. datasets/sacre_coeur/mapping/32809961_8274055477.jpg +0 -3
  26. datasets/sacre_coeur/mapping/44120379_8371960244.jpg +0 -3
  27. datasets/sacre_coeur/mapping/51091044_3486849416.jpg +0 -3
  28. datasets/sacre_coeur/mapping/60584745_2207571072.jpg +0 -3
  29. datasets/sacre_coeur/mapping/71295362_4051449754.jpg +0 -3
  30. datasets/sacre_coeur/mapping/93341989_396310999.jpg +0 -3
  31. datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot135.jpg +0 -3
  32. datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot180.jpg +0 -3
  33. datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot225.jpg +0 -3
  34. datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot270.jpg +0 -3
  35. datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot315.jpg +0 -3
  36. datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot45.jpg +0 -3
  37. datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot90.jpg +0 -3
  38. datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot135.jpg +0 -3
  39. datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot180.jpg +0 -3
  40. datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot225.jpg +0 -3
  41. datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot270.jpg +0 -3
  42. datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot315.jpg +0 -3
  43. datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot45.jpg +0 -3
  44. datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot90.jpg +0 -3
  45. datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot135.jpg +0 -3
  46. datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot180.jpg +0 -3
  47. datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot225.jpg +0 -3
  48. datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot270.jpg +0 -3
  49. datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot315.jpg +0 -3
  50. datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot45.jpg +0 -3
.gitattributes CHANGED
@@ -39,3 +39,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
39
  *.png filter=lfs diff=lfs merge=lfs -text
40
  *.bmp filter=lfs diff=lfs merge=lfs -text
41
  *.pdf filter=lfs diff=lfs merge=lfs -text
 
 
39
  *.png filter=lfs diff=lfs merge=lfs -text
40
  *.bmp filter=lfs diff=lfs merge=lfs -text
41
  *.pdf filter=lfs diff=lfs merge=lfs -text
42
+ *.whl filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -26,3 +26,4 @@ gen_example.py
26
  datasets/lines/terrace0.JPG
27
  datasets/lines/terrace1.JPG
28
  datasets/South-Building*
 
 
26
  datasets/lines/terrace0.JPG
27
  datasets/lines/terrace1.JPG
28
  datasets/South-Building*
29
+ .ruff_cache
.pre-commit-config.yaml ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # To use:
2
+ #
3
+ # pre-commit run -a
4
+ #
5
+ # Or:
6
+ #
7
+ # pre-commit run --all-files
8
+ #
9
+ # Or:
10
+ #
11
+ # pre-commit install # (runs every time you commit in git)
12
+ #
13
+ # To update this file:
14
+ #
15
+ # pre-commit autoupdate
16
+ #
17
+ # See https://github.com/pre-commit/pre-commit
18
+
19
+ ci:
20
+ autoupdate_commit_msg: "chore: update pre-commit hooks"
21
+ autofix_commit_msg: "style: pre-commit fixes"
22
+
23
+ repos:
24
+ # Standard hooks
25
+ - repo: https://github.com/pre-commit/pre-commit-hooks
26
+ rev: v5.0.0
27
+ hooks:
28
+ - id: check-added-large-files
29
+ exclude: ^imcui/third_party/
30
+ - id: check-case-conflict
31
+ exclude: ^imcui/third_party/
32
+ - id: check-merge-conflict
33
+ exclude: ^imcui/third_party/
34
+ - id: check-symlinks
35
+ exclude: ^imcui/third_party/
36
+ - id: check-yaml
37
+ exclude: ^imcui/third_party/
38
+ - id: debug-statements
39
+ exclude: ^imcui/third_party/
40
+ - id: end-of-file-fixer
41
+ exclude: ^imcui/third_party/
42
+ - id: mixed-line-ending
43
+ exclude: ^imcui/third_party/
44
+ - id: requirements-txt-fixer
45
+ exclude: ^imcui/third_party/
46
+ - id: trailing-whitespace
47
+ exclude: ^imcui/third_party/
48
+
49
+ - repo: https://github.com/astral-sh/ruff-pre-commit
50
+ rev: "v0.8.4"
51
+ hooks:
52
+ - id: ruff
53
+ args: ["--fix", "--show-fixes", "--extend-ignore=E402"]
54
+ - id: ruff-format
55
+ exclude: ^(docs|imcui/third_party/)
56
+
57
+ # Checking static types
58
+ - repo: https://github.com/pre-commit/mirrors-mypy
59
+ rev: "v1.14.0"
60
+ hooks:
61
+ - id: mypy
62
+ files: "setup.py"
63
+ args: []
64
+ additional_dependencies: [types-setuptools]
65
+ exclude: ^imcui/third_party/
66
+ # Changes tabs to spaces
67
+ - repo: https://github.com/Lucas-C/pre-commit-hooks
68
+ rev: v1.5.5
69
+ hooks:
70
+ - id: remove-tabs
71
+ exclude: ^(docs|imcui/third_party/)
72
+
73
+ # CMake formatting
74
+ - repo: https://github.com/cheshirekow/cmake-format-precommit
75
+ rev: v0.6.13
76
+ hooks:
77
+ - id: cmake-format
78
+ additional_dependencies: [pyyaml]
79
+ types: [file]
80
+ files: (\.cmake|CMakeLists.txt)(.in)?$
81
+ exclude: ^imcui/third_party/
82
+
83
+ # Suggested hook if you add a .clang-format file
84
+ - repo: https://github.com/pre-commit/mirrors-clang-format
85
+ rev: v13.0.0
86
+ hooks:
87
+ - id: clang-format
88
+ exclude: ^imcui/third_party/
Dockerfile DELETED
@@ -1,27 +0,0 @@
1
- # Use an official conda-based Python image as a parent image
2
- FROM pytorch/pytorch:2.4.0-cuda12.1-cudnn9-runtime
3
- LABEL maintainer vincentqyw
4
- ARG PYTHON_VERSION=3.10.10
5
-
6
- # Set the working directory to /code
7
- WORKDIR /code
8
-
9
- # Install Git and Git LFS
10
- RUN apt-get update && apt-get install -y git-lfs
11
- RUN git lfs install
12
-
13
- # Clone the Git repository
14
- RUN git clone https://huggingface.co/spaces/Realcat/image-matching-webui /code
15
-
16
- RUN conda create -n imw python=${PYTHON_VERSION}
17
- RUN echo "source activate imw" > ~/.bashrc
18
- ENV PATH /opt/conda/envs/imw/bin:$PATH
19
-
20
- # Make RUN commands use the new environment
21
- SHELL ["conda", "run", "-n", "imw", "/bin/bash", "-c"]
22
- RUN pip install --upgrade pip
23
- RUN pip install -r requirements.txt
24
- RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
25
-
26
- # Export port
27
- EXPOSE 7860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md DELETED
@@ -1,155 +0,0 @@
1
- ---
2
- title: Imatchui
3
- emoji: 🚀
4
- colorFrom: green
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 5.4.0
8
- app_file: app.py
9
- pinned: true
10
- license: apache-2.0
11
- ---
12
-
13
- [![Contributors][contributors-shield]][contributors-url]
14
- [![Forks][forks-shield]][forks-url]
15
- [![Stargazers][stars-shield]][stars-url]
16
- [![Issues][issues-shield]][issues-url]
17
-
18
- <p align="center">
19
- <h1 align="center"><br><ins>Image Matching WebUI</ins><br>Identify matching points between two images</h1>
20
- </p>
21
-
22
- ## Description
23
-
24
- This simple tool efficiently matches image pairs using multiple famous image matching algorithms. The tool features a Graphical User Interface (GUI) designed using [gradio](https://gradio.app/). You can effortlessly select two images and a matching algorithm and obtain a precise matching result.
25
- **Note**: the images source can be either local images or webcam images.
26
-
27
- Try it on <a href='https://huggingface.co/spaces/Realcat/image-matching-webui'><img src='https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue'></a>
28
- <a target="_blank" href="https://lightning.ai/realcat/studios/image-matching-webui">
29
- <img src="https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/app-2/studio-badge.svg" alt="Open In Studio"/>
30
- </a>
31
-
32
- Here is a demo of the tool:
33
-
34
- ![demo](assets/demo.gif)
35
-
36
- The tool currently supports various popular image matching algorithms, namely:
37
- - [x] [EfficientLoFTR](https://github.com/zju3dv/EfficientLoFTR), CVPR 2024
38
- - [x] [MASt3R](https://github.com/naver/mast3r), CVPR 2024
39
- - [x] [DUSt3R](https://github.com/naver/dust3r), CVPR 2024
40
- - [x] [OmniGlue](https://github.com/Vincentqyw/omniglue-onnx), CVPR 2024
41
- - [x] [XFeat](https://github.com/verlab/accelerated_features), CVPR 2024
42
- - [x] [RoMa](https://github.com/Vincentqyw/RoMa), CVPR 2024
43
- - [x] [DeDoDe](https://github.com/Parskatt/DeDoDe), 3DV 2024
44
- - [ ] [Mickey](https://github.com/nianticlabs/mickey), CVPR 2024
45
- - [x] [GIM](https://github.com/xuelunshen/gim), ICLR 2024
46
- - [ ] [DUSt3R](https://github.com/naver/dust3r), arXiv 2023
47
- - [x] [LightGlue](https://github.com/cvg/LightGlue), ICCV 2023
48
- - [x] [DarkFeat](https://github.com/THU-LYJ-Lab/DarkFeat), AAAI 2023
49
- - [x] [SFD2](https://github.com/feixue94/sfd2), CVPR 2023
50
- - [x] [IMP](https://github.com/feixue94/imp-release), CVPR 2023
51
- - [ ] [ASTR](https://github.com/ASTR2023/ASTR), CVPR 2023
52
- - [ ] [SEM](https://github.com/SEM2023/SEM), CVPR 2023
53
- - [ ] [DeepLSD](https://github.com/cvg/DeepLSD), CVPR 2023
54
- - [x] [GlueStick](https://github.com/cvg/GlueStick), ICCV 2023
55
- - [ ] [ConvMatch](https://github.com/SuhZhang/ConvMatch), AAAI 2023
56
- - [x] [LoFTR](https://github.com/zju3dv/LoFTR), CVPR 2021
57
- - [x] [SOLD2](https://github.com/cvg/SOLD2), CVPR 2021
58
- - [ ] [LineTR](https://github.com/yosungho/LineTR), RA-L 2021
59
- - [x] [DKM](https://github.com/Parskatt/DKM), CVPR 2023
60
- - [ ] [NCMNet](https://github.com/xinliu29/NCMNet), CVPR 2023
61
- - [x] [TopicFM](https://github.com/Vincentqyw/TopicFM), AAAI 2023
62
- - [x] [AspanFormer](https://github.com/Vincentqyw/ml-aspanformer), ECCV 2022
63
- - [x] [LANet](https://github.com/wangch-g/lanet), ACCV 2022
64
- - [ ] [LISRD](https://github.com/rpautrat/LISRD), ECCV 2022
65
- - [ ] [REKD](https://github.com/bluedream1121/REKD), CVPR 2022
66
- - [x] [CoTR](https://github.com/ubc-vision/COTR), ICCV 2021
67
- - [x] [ALIKE](https://github.com/Shiaoming/ALIKE), TMM 2022
68
- - [x] [RoRD](https://github.com/UditSinghParihar/RoRD), IROS 2021
69
- - [x] [SGMNet](https://github.com/vdvchen/SGMNet), ICCV 2021
70
- - [x] [SuperPoint](https://github.com/magicleap/SuperPointPretrainedNetwork), CVPRW 2018
71
- - [x] [SuperGlue](https://github.com/magicleap/SuperGluePretrainedNetwork), CVPR 2020
72
- - [x] [D2Net](https://github.com/Vincentqyw/d2-net), CVPR 2019
73
- - [x] [R2D2](https://github.com/naver/r2d2), NeurIPS 2019
74
- - [x] [DISK](https://github.com/cvlab-epfl/disk), NeurIPS 2020
75
- - [ ] [Key.Net](https://github.com/axelBarroso/Key.Net), ICCV 2019
76
- - [ ] [OANet](https://github.com/zjhthu/OANet), ICCV 2019
77
- - [x] [SOSNet](https://github.com/scape-research/SOSNet), CVPR 2019
78
- - [x] [HardNet](https://github.com/DagnyT/hardnet), NeurIPS 2017
79
- - [x] [SIFT](https://docs.opencv.org/4.x/da/df5/tutorial_py_sift_intro.html), IJCV 2004
80
-
81
- ## How to use
82
-
83
- ### HuggingFace / Lightning AI
84
-
85
- Just try it on <a href='https://huggingface.co/spaces/Realcat/image-matching-webui'><img src='https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue'></a>
86
- <a target="_blank" href="https://lightning.ai/realcat/studios/image-matching-webui">
87
- <img src="https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/app-2/studio-badge.svg" alt="Open In Studio"/>
88
- </a>
89
-
90
- or deploy it locally following the instructions below.
91
-
92
- ### Requirements
93
- ``` bash
94
- git clone --recursive https://github.com/Vincentqyw/image-matching-webui.git
95
- cd image-matching-webui
96
- conda env create -f environment.yaml
97
- conda activate imw
98
- ```
99
-
100
- or using [docker](https://hub.docker.com/r/vincentqin/image-matching-webui):
101
-
102
- ``` bash
103
- docker pull vincentqin/image-matching-webui:latest
104
- docker run -it -p 7860:7860 vincentqin/image-matching-webui:latest python app.py --server_name "0.0.0.0" --server_port=7860
105
- ```
106
-
107
- ### Run demo
108
- ``` bash
109
- python3 ./app.py
110
- ```
111
- then open http://localhost:7860 in your browser.
112
-
113
- ![](assets/gui.jpg)
114
-
115
- ### Add your own feature / matcher
116
-
117
- I provide an example to add local feature in [hloc/extractors/example.py](hloc/extractors/example.py). Then add feature settings in `confs` in file [hloc/extract_features.py](hloc/extract_features.py). Last step is adding some settings to `model_zoo` in file [ui/config.yaml](ui/config.yaml).
118
-
119
- ## Contributions welcome!
120
-
121
- External contributions are very much welcome. Please follow the [PEP8 style guidelines](https://www.python.org/dev/peps/pep-0008/) using a linter like flake8 (reformat using command `python -m black .`). This is a non-exhaustive list of features that might be valuable additions:
122
-
123
- - [x] add webcam support
124
- - [x] add [line feature matching](https://github.com/Vincentqyw/LineSegmentsDetection) algorithms
125
- - [x] example to add a new feature extractor / matcher
126
- - [x] ransac to filter outliers
127
- - [ ] add [rotation images](https://github.com/pidahbus/deep-image-orientation-angle-detection) options before matching
128
- - [ ] support export matches to colmap ([#issue 6](https://github.com/Vincentqyw/image-matching-webui/issues/6))
129
- - [ ] add config file to set default parameters
130
- - [ ] dynamically load models and reduce GPU overload
131
-
132
- Adding local features / matchers as submodules is very easy. For example, to add the [GlueStick](https://github.com/cvg/GlueStick):
133
-
134
- ``` bash
135
- git submodule add https://github.com/cvg/GlueStick.git third_party/GlueStick
136
- ```
137
-
138
- If remote submodule repositories are updated, don't forget to pull submodules with `git submodule update --remote`, if you only want to update one submodule, use `git submodule update --remote third_party/GlueStick`.
139
-
140
- ## Resources
141
- - [Image Matching: Local Features & Beyond](https://image-matching-workshop.github.io)
142
- - [Long-term Visual Localization](https://www.visuallocalization.net)
143
-
144
- ## Acknowledgement
145
-
146
- This code is built based on [Hierarchical-Localization](https://github.com/cvg/Hierarchical-Localization). We express our gratitude to the authors for their valuable source code.
147
-
148
- [contributors-shield]: https://img.shields.io/github/contributors/Vincentqyw/image-matching-webui.svg?style=for-the-badge
149
- [contributors-url]: https://github.com/Vincentqyw/image-matching-webui/graphs/contributors
150
- [forks-shield]: https://img.shields.io/github/forks/Vincentqyw/image-matching-webui.svg?style=for-the-badge
151
- [forks-url]: https://github.com/Vincentqyw/image-matching-webui/network/members
152
- [stars-shield]: https://img.shields.io/github/stars/Vincentqyw/image-matching-webui.svg?style=for-the-badge
153
- [stars-url]: https://github.com/Vincentqyw/image-matching-webui/stargazers
154
- [issues-shield]: https://img.shields.io/github/issues/Vincentqyw/image-matching-webui.svg?style=for-the-badge
155
- [issues-url]: https://github.com/Vincentqyw/image-matching-webui/issues
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
api/__init__.py DELETED
File without changes
api/client.py DELETED
@@ -1,225 +0,0 @@
1
- import argparse
2
- import base64
3
- import os
4
- import pickle
5
- import time
6
- from typing import Dict, List
7
-
8
- import cv2
9
- import numpy as np
10
- import requests
11
-
12
- ENDPOINT = "http://127.0.0.1:8001"
13
- if "REMOTE_URL_RAILWAY" in os.environ:
14
- ENDPOINT = os.environ["REMOTE_URL_RAILWAY"]
15
-
16
- print(f"API ENDPOINT: {ENDPOINT}")
17
-
18
- API_VERSION = f"{ENDPOINT}/version"
19
- API_URL_MATCH = f"{ENDPOINT}/v1/match"
20
- API_URL_EXTRACT = f"{ENDPOINT}/v1/extract"
21
-
22
-
23
- def read_image(path: str) -> str:
24
- """
25
- Read an image from a file, encode it as a JPEG and then as a base64 string.
26
-
27
- Args:
28
- path (str): The path to the image to read.
29
-
30
- Returns:
31
- str: The base64 encoded image.
32
- """
33
- # Read the image from the file
34
- img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
35
-
36
- # Encode the image as a png, NO COMPRESSION!!!
37
- retval, buffer = cv2.imencode(".png", img)
38
-
39
- # Encode the JPEG as a base64 string
40
- b64img = base64.b64encode(buffer).decode("utf-8")
41
-
42
- return b64img
43
-
44
-
45
- def do_api_requests(url=API_URL_EXTRACT, **kwargs):
46
- """
47
- Helper function to send an API request to the image matching service.
48
-
49
- Args:
50
- url (str): The URL of the API endpoint to use. Defaults to the
51
- feature extraction endpoint.
52
- **kwargs: Additional keyword arguments to pass to the API.
53
-
54
- Returns:
55
- List[Dict[str, np.ndarray]]: A list of dictionaries containing the
56
- extracted features. The keys are "keypoints", "descriptors", and
57
- "scores", and the values are ndarrays of shape (N, 2), (N, ?),
58
- and (N,), respectively.
59
- """
60
- # Set up the request body
61
- reqbody = {
62
- # List of image data base64 encoded
63
- "data": [],
64
- # List of maximum number of keypoints to extract from each image
65
- "max_keypoints": [100, 100],
66
- # List of timestamps for each image (not used?)
67
- "timestamps": ["0", "1"],
68
- # Whether to convert the images to grayscale
69
- "grayscale": 0,
70
- # List of image height and width
71
- "image_hw": [[640, 480], [320, 240]],
72
- # Type of feature to extract
73
- "feature_type": 0,
74
- # List of rotation angles for each image
75
- "rotates": [0.0, 0.0],
76
- # List of scale factors for each image
77
- "scales": [1.0, 1.0],
78
- # List of reference points for each image (not used)
79
- "reference_points": [[640, 480], [320, 240]],
80
- # Whether to binarize the descriptors
81
- "binarize": True,
82
- }
83
- # Update the request body with the additional keyword arguments
84
- reqbody.update(kwargs)
85
- try:
86
- # Send the request
87
- r = requests.post(url, json=reqbody)
88
- if r.status_code == 200:
89
- # Return the response
90
- return r.json()
91
- else:
92
- # Print an error message if the response code is not 200
93
- print(f"Error: Response code {r.status_code} - {r.text}")
94
- except Exception as e:
95
- # Print an error message if an exception occurs
96
- print(f"An error occurred: {e}")
97
-
98
-
99
- def send_request_match(path0: str, path1: str) -> Dict[str, np.ndarray]:
100
- """
101
- Send a request to the API to generate a match between two images.
102
-
103
- Args:
104
- path0 (str): The path to the first image.
105
- path1 (str): The path to the second image.
106
-
107
- Returns:
108
- Dict[str, np.ndarray]: A dictionary containing the generated matches.
109
- The keys are "keypoints0", "keypoints1", "matches0", and "matches1",
110
- and the values are ndarrays of shape (N, 2), (N, 2), (N, 2), and
111
- (N, 2), respectively.
112
- """
113
- files = {"image0": open(path0, "rb"), "image1": open(path1, "rb")}
114
- try:
115
- # TODO: replace files with post json
116
- response = requests.post(API_URL_MATCH, files=files)
117
- pred = {}
118
- if response.status_code == 200:
119
- pred = response.json()
120
- for key in list(pred.keys()):
121
- pred[key] = np.array(pred[key])
122
- else:
123
- print(
124
- f"Error: Response code {response.status_code} - {response.text}"
125
- )
126
- finally:
127
- files["image0"].close()
128
- files["image1"].close()
129
- return pred
130
-
131
-
132
- def send_request_extract(
133
- input_images: str, viz: bool = False
134
- ) -> List[Dict[str, np.ndarray]]:
135
- """
136
- Send a request to the API to extract features from an image.
137
-
138
- Args:
139
- input_images (str): The path to the image.
140
-
141
- Returns:
142
- List[Dict[str, np.ndarray]]: A list of dictionaries containing the
143
- extracted features. The keys are "keypoints", "descriptors", and
144
- "scores", and the values are ndarrays of shape (N, 2), (N, 128),
145
- and (N,), respectively.
146
- """
147
- image_data = read_image(input_images)
148
- inputs = {
149
- "data": [image_data],
150
- }
151
- response = do_api_requests(
152
- url=API_URL_EXTRACT,
153
- **inputs,
154
- )
155
- print("Keypoints detected: {}".format(len(response[0]["keypoints"])))
156
-
157
- # draw matching, debug only
158
- if viz:
159
- from hloc.utils.viz import plot_keypoints
160
- from ui.viz import fig2im, plot_images
161
-
162
- kpts = np.array(response[0]["keypoints_orig"])
163
- if "image_orig" in response[0].keys():
164
- img_orig = np.array(["image_orig"])
165
-
166
- output_keypoints = plot_images([img_orig], titles="titles", dpi=300)
167
- plot_keypoints([kpts])
168
- output_keypoints = fig2im(output_keypoints)
169
- cv2.imwrite(
170
- "demo_match.jpg",
171
- output_keypoints[:, :, ::-1].copy(), # RGB -> BGR
172
- )
173
- return response
174
-
175
-
176
- def get_api_version():
177
- try:
178
- response = requests.get(API_VERSION).json()
179
- print("API VERSION: {}".format(response["version"]))
180
- except Exception as e:
181
- print(f"An error occurred: {e}")
182
-
183
-
184
- if __name__ == "__main__":
185
- parser = argparse.ArgumentParser(
186
- description="Send text to stable audio server and receive generated audio."
187
- )
188
- parser.add_argument(
189
- "--image0",
190
- required=False,
191
- help="Path for the file's melody",
192
- default="datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot45.jpg",
193
- )
194
- parser.add_argument(
195
- "--image1",
196
- required=False,
197
- help="Path for the file's melody",
198
- default="datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot90.jpg",
199
- )
200
- args = parser.parse_args()
201
-
202
- # get api version
203
- get_api_version()
204
-
205
- # request match
206
- # for i in range(10):
207
- # t1 = time.time()
208
- # preds = send_request_match(args.image0, args.image1)
209
- # t2 = time.time()
210
- # print(
211
- # "Time cost1: {} seconds, matched: {}".format(
212
- # (t2 - t1), len(preds["mmkeypoints0_orig"])
213
- # )
214
- # )
215
-
216
- # request extract
217
- for i in range(10):
218
- t1 = time.time()
219
- preds = send_request_extract(args.image0)
220
- t2 = time.time()
221
- print(f"Time cost2: {(t2 - t1)} seconds")
222
-
223
- # dump preds
224
- with open("preds.pkl", "wb") as f:
225
- pickle.dump(preds, f)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
api/server.py DELETED
@@ -1,499 +0,0 @@
1
- # server.py
2
- import base64
3
- import io
4
- import sys
5
- import warnings
6
- from pathlib import Path
7
- from typing import Any, Dict, Optional, Union
8
-
9
- import cv2
10
- import matplotlib.pyplot as plt
11
- import numpy as np
12
- import torch
13
- import uvicorn
14
- from fastapi import FastAPI, File, UploadFile
15
- from fastapi.exceptions import HTTPException
16
- from fastapi.responses import JSONResponse
17
- from PIL import Image
18
-
19
- sys.path.append(str(Path(__file__).parents[1]))
20
-
21
- from api.types import ImagesInput
22
- from hloc import DEVICE, extract_features, logger, match_dense, match_features
23
- from hloc.utils.viz import add_text, plot_keypoints
24
- from ui import get_version
25
- from ui.utils import filter_matches, get_feature_model, get_model
26
- from ui.viz import display_matches, fig2im, plot_images
27
-
28
- warnings.simplefilter("ignore")
29
-
30
-
31
- def decode_base64_to_image(encoding):
32
- if encoding.startswith("data:image/"):
33
- encoding = encoding.split(";")[1].split(",")[1]
34
- try:
35
- image = Image.open(io.BytesIO(base64.b64decode(encoding)))
36
- return image
37
- except Exception as e:
38
- logger.warning(f"API cannot decode image: {e}")
39
- raise HTTPException(
40
- status_code=500, detail="Invalid encoded image"
41
- ) from e
42
-
43
-
44
- def to_base64_nparray(encoding: str) -> np.ndarray:
45
- return np.array(decode_base64_to_image(encoding)).astype("uint8")
46
-
47
-
48
- class ImageMatchingAPI(torch.nn.Module):
49
- default_conf = {
50
- "ransac": {
51
- "enable": True,
52
- "estimator": "poselib",
53
- "geometry": "homography",
54
- "method": "RANSAC",
55
- "reproj_threshold": 3,
56
- "confidence": 0.9999,
57
- "max_iter": 10000,
58
- },
59
- }
60
-
61
- def __init__(
62
- self,
63
- conf: dict = {},
64
- device: str = "cpu",
65
- detect_threshold: float = 0.015,
66
- max_keypoints: int = 1024,
67
- match_threshold: float = 0.2,
68
- ) -> None:
69
- """
70
- Initializes an instance of the ImageMatchingAPI class.
71
-
72
- Args:
73
- conf (dict): A dictionary containing the configuration parameters.
74
- device (str, optional): The device to use for computation. Defaults to "cpu".
75
- detect_threshold (float, optional): The threshold for detecting keypoints. Defaults to 0.015.
76
- max_keypoints (int, optional): The maximum number of keypoints to extract. Defaults to 1024.
77
- match_threshold (float, optional): The threshold for matching keypoints. Defaults to 0.2.
78
-
79
- Returns:
80
- None
81
- """
82
- super().__init__()
83
- self.device = device
84
- self.conf = {**self.default_conf, **conf}
85
- self._updata_config(detect_threshold, max_keypoints, match_threshold)
86
- self._init_models()
87
- if device == "cuda":
88
- memory_allocated = torch.cuda.memory_allocated(device)
89
- memory_reserved = torch.cuda.memory_reserved(device)
90
- logger.info(
91
- f"GPU memory allocated: {memory_allocated / 1024**2:.3f} MB"
92
- )
93
- logger.info(
94
- f"GPU memory reserved: {memory_reserved / 1024**2:.3f} MB"
95
- )
96
- self.pred = None
97
-
98
- def parse_match_config(self, conf):
99
- if conf["dense"]:
100
- return {
101
- **conf,
102
- "matcher": match_dense.confs.get(
103
- conf["matcher"]["model"]["name"]
104
- ),
105
- "dense": True,
106
- }
107
- else:
108
- return {
109
- **conf,
110
- "feature": extract_features.confs.get(
111
- conf["feature"]["model"]["name"]
112
- ),
113
- "matcher": match_features.confs.get(
114
- conf["matcher"]["model"]["name"]
115
- ),
116
- "dense": False,
117
- }
118
-
119
- def _updata_config(
120
- self,
121
- detect_threshold: float = 0.015,
122
- max_keypoints: int = 1024,
123
- match_threshold: float = 0.2,
124
- ):
125
- self.dense = self.conf["dense"]
126
- if self.conf["dense"]:
127
- try:
128
- self.conf["matcher"]["model"][
129
- "match_threshold"
130
- ] = match_threshold
131
- except TypeError as e:
132
- logger.error(e)
133
- else:
134
- self.conf["feature"]["model"]["max_keypoints"] = max_keypoints
135
- self.conf["feature"]["model"][
136
- "keypoint_threshold"
137
- ] = detect_threshold
138
- self.extract_conf = self.conf["feature"]
139
-
140
- self.match_conf = self.conf["matcher"]
141
-
142
- def _init_models(self):
143
- # initialize matcher
144
- self.matcher = get_model(self.match_conf)
145
- # initialize extractor
146
- if self.dense:
147
- self.extractor = None
148
- else:
149
- self.extractor = get_feature_model(self.conf["feature"])
150
-
151
- def _forward(self, img0, img1):
152
- if self.dense:
153
- pred = match_dense.match_images(
154
- self.matcher,
155
- img0,
156
- img1,
157
- self.match_conf["preprocessing"],
158
- device=self.device,
159
- )
160
- last_fixed = "{}".format( # noqa: F841
161
- self.match_conf["model"]["name"]
162
- )
163
- else:
164
- pred0 = extract_features.extract(
165
- self.extractor, img0, self.extract_conf["preprocessing"]
166
- )
167
- pred1 = extract_features.extract(
168
- self.extractor, img1, self.extract_conf["preprocessing"]
169
- )
170
- pred = match_features.match_images(self.matcher, pred0, pred1)
171
- return pred
172
-
173
- @torch.inference_mode()
174
- def extract(self, img0: np.ndarray, **kwargs) -> Dict[str, np.ndarray]:
175
- """Extract features from a single image.
176
-
177
- Args:
178
- img0 (np.ndarray): image
179
-
180
- Returns:
181
- Dict[str, np.ndarray]: feature dict
182
- """
183
-
184
- # setting prams
185
- self.extractor.conf["max_keypoints"] = kwargs.get("max_keypoints", 512)
186
- self.extractor.conf["keypoint_threshold"] = kwargs.get(
187
- "keypoint_threshold", 0.0
188
- )
189
-
190
- pred = extract_features.extract(
191
- self.extractor, img0, self.extract_conf["preprocessing"]
192
- )
193
- pred = {
194
- k: v.cpu().detach()[0].numpy() if isinstance(v, torch.Tensor) else v
195
- for k, v in pred.items()
196
- }
197
- # back to origin scale
198
- s0 = pred["original_size"] / pred["size"]
199
- pred["keypoints_orig"] = (
200
- match_features.scale_keypoints(pred["keypoints"] + 0.5, s0) - 0.5
201
- )
202
- # TODO: rotate back
203
-
204
- binarize = kwargs.get("binarize", False)
205
- if binarize:
206
- assert "descriptors" in pred
207
- pred["descriptors"] = (pred["descriptors"] > 0).astype(np.uint8)
208
- pred["descriptors"] = pred["descriptors"].T # N x DIM
209
- return pred
210
-
211
- @torch.inference_mode()
212
- def forward(
213
- self,
214
- img0: np.ndarray,
215
- img1: np.ndarray,
216
- ) -> Dict[str, np.ndarray]:
217
- """
218
- Forward pass of the image matching API.
219
-
220
- Args:
221
- img0: A 3D NumPy array of shape (H, W, C) representing the first image.
222
- Values are in the range [0, 1] and are in RGB mode.
223
- img1: A 3D NumPy array of shape (H, W, C) representing the second image.
224
- Values are in the range [0, 1] and are in RGB mode.
225
-
226
- Returns:
227
- A dictionary containing the following keys:
228
- - image0_orig: The original image 0.
229
- - image1_orig: The original image 1.
230
- - keypoints0_orig: The keypoints detected in image 0.
231
- - keypoints1_orig: The keypoints detected in image 1.
232
- - mkeypoints0_orig: The raw matches between image 0 and image 1.
233
- - mkeypoints1_orig: The raw matches between image 1 and image 0.
234
- - mmkeypoints0_orig: The RANSAC inliers in image 0.
235
- - mmkeypoints1_orig: The RANSAC inliers in image 1.
236
- - mconf: The confidence scores for the raw matches.
237
- - mmconf: The confidence scores for the RANSAC inliers.
238
- """
239
- # Take as input a pair of images (not a batch)
240
- assert isinstance(img0, np.ndarray)
241
- assert isinstance(img1, np.ndarray)
242
- self.pred = self._forward(img0, img1)
243
- if self.conf["ransac"]["enable"]:
244
- self.pred = self._geometry_check(self.pred)
245
- return self.pred
246
-
247
- def _geometry_check(
248
- self,
249
- pred: Dict[str, Any],
250
- ) -> Dict[str, Any]:
251
- """
252
- Filter matches using RANSAC. If keypoints are available, filter by keypoints.
253
- If lines are available, filter by lines. If both keypoints and lines are
254
- available, filter by keypoints.
255
-
256
- Args:
257
- pred (Dict[str, Any]): dict of matches, including original keypoints.
258
- See :func:`filter_matches` for the expected keys.
259
-
260
- Returns:
261
- Dict[str, Any]: filtered matches
262
- """
263
- pred = filter_matches(
264
- pred,
265
- ransac_method=self.conf["ransac"]["method"],
266
- ransac_reproj_threshold=self.conf["ransac"]["reproj_threshold"],
267
- ransac_confidence=self.conf["ransac"]["confidence"],
268
- ransac_max_iter=self.conf["ransac"]["max_iter"],
269
- )
270
- return pred
271
-
272
- def visualize(
273
- self,
274
- log_path: Optional[Path] = None,
275
- ) -> None:
276
- """
277
- Visualize the matches.
278
-
279
- Args:
280
- log_path (Path, optional): The directory to save the images. Defaults to None.
281
-
282
- Returns:
283
- None
284
- """
285
- if self.conf["dense"]:
286
- postfix = str(self.conf["matcher"]["model"]["name"])
287
- else:
288
- postfix = "{}_{}".format(
289
- str(self.conf["feature"]["model"]["name"]),
290
- str(self.conf["matcher"]["model"]["name"]),
291
- )
292
- titles = [
293
- "Image 0 - Keypoints",
294
- "Image 1 - Keypoints",
295
- ]
296
- pred: Dict[str, Any] = self.pred
297
- image0: np.ndarray = pred["image0_orig"]
298
- image1: np.ndarray = pred["image1_orig"]
299
- output_keypoints: np.ndarray = plot_images(
300
- [image0, image1], titles=titles, dpi=300
301
- )
302
- if (
303
- "keypoints0_orig" in pred.keys()
304
- and "keypoints1_orig" in pred.keys()
305
- ):
306
- plot_keypoints([pred["keypoints0_orig"], pred["keypoints1_orig"]])
307
- text: str = (
308
- f"# keypoints0: {len(pred['keypoints0_orig'])} \n"
309
- + f"# keypoints1: {len(pred['keypoints1_orig'])}"
310
- )
311
- add_text(0, text, fs=15)
312
- output_keypoints = fig2im(output_keypoints)
313
- # plot images with raw matches
314
- titles = [
315
- "Image 0 - Raw matched keypoints",
316
- "Image 1 - Raw matched keypoints",
317
- ]
318
- output_matches_raw, num_matches_raw = display_matches(
319
- pred, titles=titles, tag="KPTS_RAW"
320
- )
321
- # plot images with ransac matches
322
- titles = [
323
- "Image 0 - Ransac matched keypoints",
324
- "Image 1 - Ransac matched keypoints",
325
- ]
326
- output_matches_ransac, num_matches_ransac = display_matches(
327
- pred, titles=titles, tag="KPTS_RANSAC"
328
- )
329
- if log_path is not None:
330
- img_keypoints_path: Path = log_path / f"img_keypoints_{postfix}.png"
331
- img_matches_raw_path: Path = (
332
- log_path / f"img_matches_raw_{postfix}.png"
333
- )
334
- img_matches_ransac_path: Path = (
335
- log_path / f"img_matches_ransac_{postfix}.png"
336
- )
337
- cv2.imwrite(
338
- str(img_keypoints_path),
339
- output_keypoints[:, :, ::-1].copy(), # RGB -> BGR
340
- )
341
- cv2.imwrite(
342
- str(img_matches_raw_path),
343
- output_matches_raw[:, :, ::-1].copy(), # RGB -> BGR
344
- )
345
- cv2.imwrite(
346
- str(img_matches_ransac_path),
347
- output_matches_ransac[:, :, ::-1].copy(), # RGB -> BGR
348
- )
349
- plt.close("all")
350
-
351
-
352
- class ImageMatchingService:
353
- def __init__(self, conf: dict, device: str):
354
- self.conf = conf
355
- self.api = ImageMatchingAPI(conf=conf, device=device)
356
- self.app = FastAPI()
357
- self.register_routes()
358
-
359
- def register_routes(self):
360
-
361
- @self.app.get("/version")
362
- async def version():
363
- return {"version": get_version()}
364
-
365
- @self.app.post("/v1/match")
366
- async def match(
367
- image0: UploadFile = File(...), image1: UploadFile = File(...)
368
- ):
369
- """
370
- Handle the image matching request and return the processed result.
371
-
372
- Args:
373
- image0 (UploadFile): The first image file for matching.
374
- image1 (UploadFile): The second image file for matching.
375
-
376
- Returns:
377
- JSONResponse: A JSON response containing the filtered match results
378
- or an error message in case of failure.
379
- """
380
- try:
381
- # Load the images from the uploaded files
382
- image0_array = self.load_image(image0)
383
- image1_array = self.load_image(image1)
384
-
385
- # Perform image matching using the API
386
- output = self.api(image0_array, image1_array)
387
-
388
- # Keys to skip in the output
389
- skip_keys = ["image0_orig", "image1_orig"]
390
-
391
- # Postprocess the output to filter unwanted data
392
- pred = self.postprocess(output, skip_keys)
393
-
394
- # Return the filtered prediction as a JSON response
395
- return JSONResponse(content=pred)
396
- except Exception as e:
397
- # Return an error message with status code 500 in case of exception
398
- return JSONResponse(content={"error": str(e)}, status_code=500)
399
-
400
- @self.app.post("/v1/extract")
401
- async def extract(input_info: ImagesInput):
402
- """
403
- Extract keypoints and descriptors from images.
404
-
405
- Args:
406
- input_info: An object containing the image data and options.
407
-
408
- Returns:
409
- A list of dictionaries containing the keypoints and descriptors.
410
- """
411
- try:
412
- preds = []
413
- for i, input_image in enumerate(input_info.data):
414
- # Load the image from the input data
415
- image_array = to_base64_nparray(input_image)
416
- # Extract keypoints and descriptors
417
- output = self.api.extract(
418
- image_array,
419
- max_keypoints=input_info.max_keypoints[i],
420
- binarize=input_info.binarize,
421
- )
422
- # Do not return the original image and image_orig
423
- # skip_keys = ["image", "image_orig"]
424
- skip_keys = []
425
-
426
- # Postprocess the output
427
- pred = self.postprocess(output, skip_keys)
428
- preds.append(pred)
429
- # Return the list of extracted features
430
- return JSONResponse(content=preds)
431
- except Exception as e:
432
- # Return an error message if an exception occurs
433
- return JSONResponse(content={"error": str(e)}, status_code=500)
434
-
435
- def load_image(self, file_path: Union[str, UploadFile]) -> np.ndarray:
436
- """
437
- Reads an image from a file path or an UploadFile object.
438
-
439
- Args:
440
- file_path: A file path or an UploadFile object.
441
-
442
- Returns:
443
- A numpy array representing the image.
444
- """
445
- if isinstance(file_path, str):
446
- file_path = Path(file_path).resolve(strict=False)
447
- else:
448
- file_path = file_path.file
449
- with Image.open(file_path) as img:
450
- image_array = np.array(img)
451
- return image_array
452
-
453
- def postprocess(
454
- self, output: dict, skip_keys: list, binarize: bool = True
455
- ) -> dict:
456
- pred = {}
457
- for key, value in output.items():
458
- if key in skip_keys:
459
- continue
460
- if isinstance(value, np.ndarray):
461
- pred[key] = value.tolist()
462
- return pred
463
-
464
- def run(self, host: str = "0.0.0.0", port: int = 8001):
465
- uvicorn.run(self.app, host=host, port=port)
466
-
467
-
468
- if __name__ == "__main__":
469
- conf = {
470
- "feature": {
471
- "output": "feats-superpoint-n4096-rmax1600",
472
- "model": {
473
- "name": "superpoint",
474
- "nms_radius": 3,
475
- "max_keypoints": 4096,
476
- "keypoint_threshold": 0.005,
477
- },
478
- "preprocessing": {
479
- "grayscale": True,
480
- "force_resize": True,
481
- "resize_max": 1600,
482
- "width": 640,
483
- "height": 480,
484
- "dfactor": 8,
485
- },
486
- },
487
- "matcher": {
488
- "output": "matches-NN-mutual",
489
- "model": {
490
- "name": "nearest_neighbor",
491
- "do_mutual_check": True,
492
- "match_threshold": 0.2,
493
- },
494
- },
495
- "dense": False,
496
- }
497
-
498
- service = ImageMatchingService(conf=conf, device=DEVICE)
499
- service.run()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
api/test/CMakeLists.txt DELETED
@@ -1,16 +0,0 @@
1
- cmake_minimum_required(VERSION 3.10)
2
- project(imatchui)
3
-
4
- set(OpenCV_DIR /usr/include/opencv4)
5
- find_package(OpenCV REQUIRED)
6
-
7
- find_package(Boost REQUIRED COMPONENTS system)
8
- if(Boost_FOUND)
9
- include_directories(${Boost_INCLUDE_DIRS})
10
- endif()
11
-
12
- add_executable(client client.cpp)
13
-
14
- target_include_directories(client PRIVATE ${Boost_LIBRARIES} ${OpenCV_INCLUDE_DIRS})
15
-
16
- target_link_libraries(client PRIVATE curl jsoncpp b64 ${OpenCV_LIBS})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
api/test/build_and_run.sh DELETED
@@ -1,16 +0,0 @@
1
- # g++ main.cpp -I/usr/include/opencv4 -lcurl -ljsoncpp -lb64 -lopencv_core -lopencv_imgcodecs -o main
2
- # sudo apt-get update
3
- # sudo apt-get install libboost-all-dev -y
4
- # sudo apt-get install libcurl4-openssl-dev libjsoncpp-dev libb64-dev libopencv-dev -y
5
-
6
- cd build
7
- cmake ..
8
- make -j12
9
-
10
- echo " ======== RUN DEMO ========"
11
-
12
- ./client
13
-
14
- echo " ======== END DEMO ========"
15
-
16
- cd ..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
api/test/client.cpp DELETED
@@ -1,84 +0,0 @@
1
- #include <curl/curl.h>
2
- #include <opencv2/opencv.hpp>
3
- #include "helper.h"
4
-
5
- int main() {
6
- std::string img_path = "../../../datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot45.jpg";
7
- cv::Mat original_img = cv::imread(img_path, cv::IMREAD_GRAYSCALE);
8
-
9
- if (original_img.empty()) {
10
- throw std::runtime_error("Failed to decode image");
11
- }
12
-
13
- // Convert the image to Base64
14
- std::string base64_img = image_to_base64(original_img);
15
-
16
- // Convert the Base64 back to an image
17
- cv::Mat decoded_img = base64_to_image(base64_img);
18
- cv::imwrite("decoded_image.jpg", decoded_img);
19
- cv::imwrite("original_img.jpg", original_img);
20
-
21
- // The images should be identical
22
- if (cv::countNonZero(original_img != decoded_img) != 0) {
23
- std::cerr << "The images are not identical" << std::endl;
24
- return -1;
25
- } else {
26
- std::cout << "The images are identical!" << std::endl;
27
- }
28
-
29
- // construct params
30
- APIParams params{
31
- .data = {base64_img},
32
- .max_keypoints = {100, 100},
33
- .timestamps = {"0", "1"},
34
- .grayscale = {0},
35
- .image_hw = {{480, 640}, {240, 320}},
36
- .feature_type = 0,
37
- .rotates = {0.0f, 0.0f},
38
- .scales = {1.0f, 1.0f},
39
- .reference_points = {
40
- {1.23e+2f, 1.2e+1f},
41
- {5.0e-1f, 3.0e-1f},
42
- {2.3e+2f, 2.2e+1f},
43
- {6.0e-1f, 4.0e-1f}
44
- },
45
- .binarize = {1}
46
- };
47
-
48
- KeyPointResults kpts_results;
49
-
50
- // Convert the parameters to JSON
51
- Json::Value jsonData = paramsToJson(params);
52
- std::string url = "http://127.0.0.1:8001/v1/extract";
53
- Json::StreamWriterBuilder writer;
54
- std::string output = Json::writeString(writer, jsonData);
55
-
56
- CURL* curl;
57
- CURLcode res;
58
- std::string readBuffer;
59
-
60
- curl_global_init(CURL_GLOBAL_DEFAULT);
61
- curl = curl_easy_init();
62
- if (curl) {
63
- struct curl_slist* hs = NULL;
64
- hs = curl_slist_append(hs, "Content-Type: application/json");
65
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs);
66
- curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
67
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, output.c_str());
68
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
69
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
70
- res = curl_easy_perform(curl);
71
-
72
- if (res != CURLE_OK)
73
- fprintf(stderr, "curl_easy_perform() failed: %s\n",
74
- curl_easy_strerror(res));
75
- else {
76
- // std::cout << "Response from server: " << readBuffer << std::endl;
77
- kpts_results = decode_response(readBuffer);
78
- }
79
- curl_easy_cleanup(curl);
80
- }
81
- curl_global_cleanup();
82
-
83
- return 0;
84
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
api/test/helper.h DELETED
@@ -1,410 +0,0 @@
1
-
2
- #include <sstream>
3
- #include <fstream>
4
- #include <vector>
5
- #include <b64/encode.h>
6
- #include <jsoncpp/json/json.h>
7
- #include <opencv2/opencv.hpp>
8
-
9
- // base64 to image
10
- #include <boost/archive/iterators/binary_from_base64.hpp>
11
- #include <boost/archive/iterators/transform_width.hpp>
12
- #include <boost/archive/iterators/base64_from_binary.hpp>
13
-
14
- /// Parameters used in the API
15
- struct APIParams {
16
- /// A list of images, base64 encoded
17
- std::vector<std::string> data;
18
-
19
- /// The maximum number of keypoints to detect for each image
20
- std::vector<int> max_keypoints;
21
-
22
- /// The timestamps of the images
23
- std::vector<std::string> timestamps;
24
-
25
- /// Whether to convert the images to grayscale
26
- bool grayscale;
27
-
28
- /// The height and width of each image
29
- std::vector<std::vector<int>> image_hw;
30
-
31
- /// The type of feature detector to use
32
- int feature_type;
33
-
34
- /// The rotations of the images
35
- std::vector<double> rotates;
36
-
37
- /// The scales of the images
38
- std::vector<double> scales;
39
-
40
- /// The reference points of the images
41
- std::vector<std::vector<float>> reference_points;
42
-
43
- /// Whether to binarize the descriptors
44
- bool binarize;
45
- };
46
-
47
- /**
48
- * @brief Contains the results of a keypoint detector.
49
- *
50
- * @details Stores the keypoints and descriptors for each image.
51
- */
52
- class KeyPointResults {
53
- public:
54
- KeyPointResults() {}
55
-
56
- /**
57
- * @brief Constructor.
58
- *
59
- * @param kp The keypoints for each image.
60
- */
61
- KeyPointResults(const std::vector<std::vector<cv::KeyPoint>>& kp,
62
- const std::vector<cv::Mat>& desc)
63
- : keypoints(kp), descriptors(desc) {}
64
-
65
- /**
66
- * @brief Append keypoints to the result.
67
- *
68
- * @param kpts The keypoints to append.
69
- */
70
- inline void append_keypoints(std::vector<cv::KeyPoint>& kpts) {
71
- keypoints.emplace_back(kpts);
72
- }
73
-
74
- /**
75
- * @brief Append descriptors to the result.
76
- *
77
- * @param desc The descriptors to append.
78
- */
79
- inline void append_descriptors(cv::Mat& desc) {
80
- descriptors.emplace_back(desc);
81
- }
82
-
83
- /**
84
- * @brief Get the keypoints.
85
- *
86
- * @return The keypoints.
87
- */
88
- inline std::vector<std::vector<cv::KeyPoint>> get_keypoints() {
89
- return keypoints;
90
- }
91
-
92
- /**
93
- * @brief Get the descriptors.
94
- *
95
- * @return The descriptors.
96
- */
97
- inline std::vector<cv::Mat> get_descriptors() {
98
- return descriptors;
99
- }
100
-
101
- private:
102
- std::vector<std::vector<cv::KeyPoint>> keypoints;
103
- std::vector<cv::Mat> descriptors;
104
- std::vector<std::vector<float>> scores;
105
- };
106
-
107
-
108
- /**
109
- * @brief Decodes a base64 encoded string.
110
- *
111
- * @param base64 The base64 encoded string to decode.
112
- * @return The decoded string.
113
- */
114
- std::string base64_decode(const std::string& base64) {
115
- using namespace boost::archive::iterators;
116
- using It = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
117
-
118
- // Find the position of the last non-whitespace character
119
- auto end = base64.find_last_not_of(" \t\n\r");
120
- if (end != std::string::npos) {
121
- // Move one past the last non-whitespace character
122
- end += 1;
123
- }
124
-
125
- // Decode the base64 string and return the result
126
- return std::string(It(base64.begin()), It(base64.begin() + end));
127
- }
128
-
129
-
130
-
131
- /**
132
- * @brief Decodes a base64 string into an OpenCV image
133
- *
134
- * @param base64 The base64 encoded string
135
- * @return The decoded OpenCV image
136
- */
137
- cv::Mat base64_to_image(const std::string& base64) {
138
- // Decode the base64 string
139
- std::string decodedStr = base64_decode(base64);
140
-
141
- // Decode the image
142
- std::vector<uchar> data(decodedStr.begin(), decodedStr.end());
143
- cv::Mat img = cv::imdecode(data, cv::IMREAD_GRAYSCALE);
144
-
145
- // Check for errors
146
- if (img.empty()) {
147
- throw std::runtime_error("Failed to decode image");
148
- }
149
-
150
- return img;
151
- }
152
-
153
-
154
- /**
155
- * @brief Encodes an OpenCV image into a base64 string
156
- *
157
- * This function takes an OpenCV image and encodes it into a base64 string.
158
- * The image is first encoded as a PNG image, and then the resulting
159
- * bytes are encoded as a base64 string.
160
- *
161
- * @param img The OpenCV image
162
- * @return The base64 encoded string
163
- *
164
- * @throws std::runtime_error if the image is empty or encoding fails
165
- */
166
- std::string image_to_base64(cv::Mat &img) {
167
- if (img.empty()) {
168
- throw std::runtime_error("Failed to read image");
169
- }
170
-
171
- // Encode the image as a PNG
172
- std::vector<uchar> buf;
173
- if (!cv::imencode(".png", img, buf)) {
174
- throw std::runtime_error("Failed to encode image");
175
- }
176
-
177
- // Encode the bytes as a base64 string
178
- using namespace boost::archive::iterators;
179
- using It = base64_from_binary<transform_width<std::vector<uchar>::const_iterator, 6, 8>>;
180
- std::string base64(It(buf.begin()), It(buf.end()));
181
-
182
- // Pad the string with '=' characters to a multiple of 4 bytes
183
- base64.append((3 - buf.size() % 3) % 3, '=');
184
-
185
- return base64;
186
- }
187
-
188
-
189
- /**
190
- * @brief Callback function for libcurl to write data to a string
191
- *
192
- * This function is used as a callback for libcurl to write data to a string.
193
- * It takes the contents, size, and nmemb as parameters, and writes the data to
194
- * the string.
195
- *
196
- * @param contents The data to write
197
- * @param size The size of the data
198
- * @param nmemb The number of members in the data
199
- * @param s The string to write the data to
200
- * @return The number of bytes written
201
- */
202
- size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* s) {
203
- size_t newLength = size * nmemb;
204
- try {
205
- // Resize the string to fit the new data
206
- s->resize(s->size() + newLength);
207
- } catch (std::bad_alloc& e) {
208
- // If there's an error allocating memory, return 0
209
- return 0;
210
- }
211
-
212
- // Copy the data to the string
213
- std::copy(static_cast<const char*>(contents),
214
- static_cast<const char*>(contents) + newLength,
215
- s->begin() + s->size() - newLength);
216
- return newLength;
217
- }
218
-
219
- // Helper functions
220
-
221
- /**
222
- * @brief Helper function to convert a type to a Json::Value
223
- *
224
- * This function takes a value of type T and converts it to a Json::Value.
225
- * It is used to simplify the process of converting a type to a Json::Value.
226
- *
227
- * @param val The value to convert
228
- * @return The converted Json::Value
229
- */
230
- template <typename T>
231
- Json::Value toJson(const T& val) {
232
- return Json::Value(val);
233
- }
234
-
235
- /**
236
- * @brief Converts a vector to a Json::Value
237
- *
238
- * This function takes a vector of type T and converts it to a Json::Value.
239
- * Each element in the vector is appended to the Json::Value array.
240
- *
241
- * @param vec The vector to convert to Json::Value
242
- * @return The Json::Value representing the vector
243
- */
244
- template <typename T>
245
- Json::Value vectorToJson(const std::vector<T>& vec) {
246
- Json::Value json(Json::arrayValue);
247
- for (const auto& item : vec) {
248
- json.append(item);
249
- }
250
- return json;
251
- }
252
-
253
- /**
254
- * @brief Converts a nested vector to a Json::Value
255
- *
256
- * This function takes a nested vector of type T and converts it to a Json::Value.
257
- * Each sub-vector is converted to a Json::Value array and appended to the main Json::Value array.
258
- *
259
- * @param vec The nested vector to convert to Json::Value
260
- * @return The Json::Value representing the nested vector
261
- */
262
- template <typename T>
263
- Json::Value nestedVectorToJson(const std::vector<std::vector<T>>& vec) {
264
- Json::Value json(Json::arrayValue);
265
- for (const auto& subVec : vec) {
266
- json.append(vectorToJson(subVec));
267
- }
268
- return json;
269
- }
270
-
271
-
272
-
273
- /**
274
- * @brief Converts the APIParams struct to a Json::Value
275
- *
276
- * This function takes an APIParams struct and converts it to a Json::Value.
277
- * The Json::Value is a JSON object with the following fields:
278
- * - data: a JSON array of base64 encoded images
279
- * - max_keypoints: a JSON array of integers, max number of keypoints for each image
280
- * - timestamps: a JSON array of timestamps, one for each image
281
- * - grayscale: a JSON boolean, whether to convert images to grayscale
282
- * - image_hw: a nested JSON array, each sub-array contains the height and width of an image
283
- * - feature_type: a JSON integer, the type of feature detector to use
284
- * - rotates: a JSON array of doubles, the rotation of each image
285
- * - scales: a JSON array of doubles, the scale of each image
286
- * - reference_points: a nested JSON array, each sub-array contains the reference points of an image
287
- * - binarize: a JSON boolean, whether to binarize the descriptors
288
- *
289
- * @param params The APIParams struct to convert
290
- * @return The Json::Value representing the APIParams struct
291
- */
292
- Json::Value paramsToJson(const APIParams& params) {
293
- Json::Value json;
294
- json["data"] = vectorToJson(params.data);
295
- json["max_keypoints"] = vectorToJson(params.max_keypoints);
296
- json["timestamps"] = vectorToJson(params.timestamps);
297
- json["grayscale"] = toJson(params.grayscale);
298
- json["image_hw"] = nestedVectorToJson(params.image_hw);
299
- json["feature_type"] = toJson(params.feature_type);
300
- json["rotates"] = vectorToJson(params.rotates);
301
- json["scales"] = vectorToJson(params.scales);
302
- json["reference_points"] = nestedVectorToJson(params.reference_points);
303
- json["binarize"] = toJson(params.binarize);
304
- return json;
305
- }
306
-
307
- template<typename T>
308
- cv::Mat jsonToMat(Json::Value json) {
309
- int rows = json.size();
310
- int cols = json[0].size();
311
-
312
- // Create a single array to hold all the data.
313
- std::vector<T> data;
314
- data.reserve(rows * cols);
315
-
316
- for (int i = 0; i < rows; i++) {
317
- for (int j = 0; j < cols; j++) {
318
- data.push_back(static_cast<T>(json[i][j].asInt()));
319
- }
320
- }
321
-
322
- // Create a cv::Mat object that points to the data.
323
- cv::Mat mat(rows, cols, CV_8UC1, data.data()); // Change the type if necessary.
324
- // cv::Mat mat(cols, rows,CV_8UC1, data.data()); // Change the type if necessary.
325
-
326
- return mat;
327
- }
328
-
329
-
330
-
331
- /**
332
- * @brief Decodes the response of the server and prints the keypoints
333
- *
334
- * This function takes the response of the server, a JSON string, and decodes
335
- * it. It then prints the keypoints and draws them on the original image.
336
- *
337
- * @param response The response of the server
338
- * @return The keypoints and descriptors
339
- */
340
- KeyPointResults decode_response(const std::string& response, bool viz=true) {
341
- Json::CharReaderBuilder builder;
342
- Json::CharReader* reader = builder.newCharReader();
343
-
344
- Json::Value jsonData;
345
- std::string errors;
346
-
347
- // Parse the JSON response
348
- bool parsingSuccessful = reader->parse(response.c_str(),
349
- response.c_str() + response.size(), &jsonData, &errors);
350
- delete reader;
351
-
352
- if (!parsingSuccessful) {
353
- // Handle error
354
- std::cout << "Failed to parse the JSON, errors:" << std::endl;
355
- std::cout << errors << std::endl;
356
- return KeyPointResults();
357
- }
358
-
359
- KeyPointResults kpts_results;
360
-
361
- // Iterate over the images
362
- for (const auto& jsonItem : jsonData) {
363
- auto jkeypoints = jsonItem["keypoints"];
364
- auto jkeypoints_orig = jsonItem["keypoints_orig"];
365
- auto jdescriptors = jsonItem["descriptors"];
366
- auto jscores = jsonItem["scores"];
367
- auto jimageSize = jsonItem["image_size"];
368
- auto joriginalSize = jsonItem["original_size"];
369
- auto jsize = jsonItem["size"];
370
-
371
- std::vector<cv::KeyPoint> vkeypoints;
372
- std::vector<float> vscores;
373
-
374
- // Iterate over the keypoints
375
- int counter = 0;
376
- for (const auto& keypoint : jkeypoints_orig) {
377
- if (counter < 10) {
378
- // Print the first 10 keypoints
379
- std::cout << keypoint[0].asFloat() << ", "
380
- << keypoint[1].asFloat() << std::endl;
381
- }
382
- counter++;
383
- // Convert the Json::Value to a cv::KeyPoint
384
- vkeypoints.emplace_back(cv::KeyPoint(keypoint[0].asFloat(),
385
- keypoint[1].asFloat(), 0.0));
386
- }
387
-
388
- if (viz && jsonItem.isMember("image_orig")) {
389
-
390
- auto jimg_orig = jsonItem["image_orig"];
391
- cv::Mat img = jsonToMat<uchar>(jimg_orig);
392
- cv::imwrite("viz_image_orig.jpg", img);
393
-
394
- // Draw keypoints on the image
395
- cv::Mat imgWithKeypoints;
396
- cv::drawKeypoints(img, vkeypoints,
397
- imgWithKeypoints, cv::Scalar(0, 0, 255));
398
-
399
- // Write the image with keypoints
400
- std::string filename = "viz_image_orig_keypoints.jpg";
401
- cv::imwrite(filename, imgWithKeypoints);
402
- }
403
-
404
- // Iterate over the descriptors
405
- cv::Mat descriptors = jsonToMat<uchar>(jdescriptors);
406
- kpts_results.append_keypoints(vkeypoints);
407
- kpts_results.append_descriptors(descriptors);
408
- }
409
- return kpts_results;
410
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
api/types.py DELETED
@@ -1,16 +0,0 @@
1
- from typing import List
2
-
3
- from pydantic import BaseModel
4
-
5
-
6
- class ImagesInput(BaseModel):
7
- data: List[str] = []
8
- max_keypoints: List[int] = []
9
- timestamps: List[str] = []
10
- grayscale: bool = False
11
- image_hw: List[List[int]] = [[], []]
12
- feature_type: int = 0
13
- rotates: List[float] = []
14
- scales: List[float] = []
15
- reference_points: List[List[float]] = []
16
- binarize: bool = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -1,6 +1,6 @@
1
  import argparse
2
  from pathlib import Path
3
- from ui.app_class import ImageMatchingApp
4
 
5
  if __name__ == "__main__":
6
  parser = argparse.ArgumentParser()
@@ -19,10 +19,8 @@ if __name__ == "__main__":
19
  parser.add_argument(
20
  "--config",
21
  type=str,
22
- default=Path(__file__).parent / "ui/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()
 
1
  import argparse
2
  from pathlib import Path
3
+ from imcui.ui.app_class import ImageMatchingApp
4
 
5
  if __name__ == "__main__":
6
  parser = argparse.ArgumentParser()
 
19
  parser.add_argument(
20
  "--config",
21
  type=str,
22
+ default=Path(__file__).parent / "config/config.yaml",
23
  help="config file",
24
  )
25
  args = parser.parse_args()
26
+ ImageMatchingApp(args.server_name, args.server_port, config=args.config).run()
 
 
assets/gui.jpg DELETED

Git LFS Details

  • SHA256: a783162639d05631f34e8e3e9a7df682197a76f675265ebbaa639927e08473f7
  • Pointer size: 132 Bytes
  • Size of remote file: 1.67 MB
assets/logo.webp DELETED
Binary file (404 kB)
 
build_docker.sh DELETED
@@ -1,3 +0,0 @@
1
- docker build -t image-matching-webui:latest . --no-cache
2
- docker tag image-matching-webui:latest vincentqin/image-matching-webui:latest
3
- docker push vincentqin/image-matching-webui:latest
 
 
 
 
{ui → config}/config.yaml RENAMED
@@ -39,7 +39,7 @@ matcher_zoo:
39
  project: https://dust3r.europe.naverlabs.com
40
  display: true
41
  DUSt3R:
42
- # TODO: duster is under development
43
  enable: true
44
  # skip_ci: true
45
  matcher: duster
 
39
  project: https://dust3r.europe.naverlabs.com
40
  display: true
41
  DUSt3R:
42
+ # TODO: duster is under development
43
  enable: true
44
  # skip_ci: true
45
  matcher: duster
datasets/.gitignore DELETED
File without changes
datasets/sacre_coeur/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Sacre Coeur demo
2
-
3
- We provide here a subset of images depicting the Sacre Coeur. These images were obtained from the [Image Matching Challenge 2021](https://www.cs.ubc.ca/research/image-matching-challenge/2021/data/) and were originally collected by the [Yahoo Flickr Creative Commons 100M (YFCC) dataset](https://multimediacommons.wordpress.com/yfcc100m-core-dataset/).
 
 
 
 
datasets/sacre_coeur/mapping/02928139_3448003521.jpg DELETED

Git LFS Details

  • SHA256: 4f52d9dcdb3ba9d8cf025025fb1be3f8f8d1ba0e0d84ab7eeb271215589ca608
  • Pointer size: 131 Bytes
  • Size of remote file: 518 kB
datasets/sacre_coeur/mapping/03903474_1471484089.jpg DELETED

Git LFS Details

  • SHA256: 70969e7b24bbc8fe8de0a114d245ab9322df2bf10cd8533cc0d3ec6ec5f59c15
  • Pointer size: 131 Bytes
  • Size of remote file: 349 kB
datasets/sacre_coeur/mapping/10265353_3838484249.jpg DELETED

Git LFS Details

  • SHA256: d9c8ad3c8694703c08da1a4ec3980e1a35011690368f05e167092b33be8ff25c
  • Pointer size: 131 Bytes
  • Size of remote file: 455 kB
datasets/sacre_coeur/mapping/17295357_9106075285.jpg DELETED

Git LFS Details

  • SHA256: 54dff1885bf44b5c0e0c0ce702220832e99e5b30f38462d1ef5b9d4a0d794f98
  • Pointer size: 131 Bytes
  • Size of remote file: 535 kB
datasets/sacre_coeur/mapping/32809961_8274055477.jpg DELETED

Git LFS Details

  • SHA256: 4c2bf90ab409dfed11b7c112c120917a51141010335530c8607b71010d3919fa
  • Pointer size: 131 Bytes
  • Size of remote file: 458 kB
datasets/sacre_coeur/mapping/44120379_8371960244.jpg DELETED

Git LFS Details

  • SHA256: fc7bbe1e4b47eeebf986e658d44704dbcce902a3af0d4853ef2e540c95a77659
  • Pointer size: 131 Bytes
  • Size of remote file: 358 kB
datasets/sacre_coeur/mapping/51091044_3486849416.jpg DELETED

Git LFS Details

  • SHA256: fe4d46cb3feab25b4d184dca745921d7de7dfd18a0f3343660d8d3de07a0c054
  • Pointer size: 131 Bytes
  • Size of remote file: 492 kB
datasets/sacre_coeur/mapping/60584745_2207571072.jpg DELETED

Git LFS Details

  • SHA256: 186e6b8a426403e56a608429f7f63d5a56d32a895487f3da7cb9ceaff97f563f
  • Pointer size: 131 Bytes
  • Size of remote file: 471 kB
datasets/sacre_coeur/mapping/71295362_4051449754.jpg DELETED

Git LFS Details

  • SHA256: f7e7a59fab6f497bd94b066589a62cb74ceffe63ff3c8cd77b9b6b39bfc66bae
  • Pointer size: 131 Bytes
  • Size of remote file: 369 kB
datasets/sacre_coeur/mapping/93341989_396310999.jpg DELETED

Git LFS Details

  • SHA256: 2932ea734fb26d5417aba1b1891760299ddf16facfd6a76a3712a7f11652e1f6
  • Pointer size: 131 Bytes
  • Size of remote file: 365 kB
datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot135.jpg DELETED

Git LFS Details

  • SHA256: 2b73c04d53516237028bd6c74011d2b94eb09a99e2741ee2c491f070a4b9dd28
  • Pointer size: 131 Bytes
  • Size of remote file: 134 kB
datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot180.jpg DELETED

Git LFS Details

  • SHA256: 85a3bac5d7072d1bb06022b052d4c7b27e7216a8e02688ab5d9d954799254a06
  • Pointer size: 131 Bytes
  • Size of remote file: 128 kB
datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot225.jpg DELETED

Git LFS Details

  • SHA256: d3d1ccde193e18620aa6da0aec5ddbbe612f30f2b398cd596e6585b9e114e45f
  • Pointer size: 131 Bytes
  • Size of remote file: 134 kB
datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot270.jpg DELETED

Git LFS Details

  • SHA256: b4d238d8a052162da641b0d54506c85641c91f6f95cdf471e79147f4c373162d
  • Pointer size: 131 Bytes
  • Size of remote file: 115 kB
datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot315.jpg DELETED

Git LFS Details

  • SHA256: 2110adbf1d114c498c5d011adfbe779c813e894500ff429a5b365447a3d9d106
  • Pointer size: 131 Bytes
  • Size of remote file: 134 kB
datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot45.jpg DELETED

Git LFS Details

  • SHA256: bbf08e8eadcadeed24a6843cf79ee0edf1771d09c71b7e1d387a997ea1922cfb
  • Pointer size: 131 Bytes
  • Size of remote file: 133 kB
datasets/sacre_coeur/mapping_rot/02928139_3448003521_rot90.jpg DELETED

Git LFS Details

  • SHA256: ede5a1cf1b99a230b407e24e7bf1a7926cf684d889674d085b299f8937ee3ae3
  • Pointer size: 131 Bytes
  • Size of remote file: 115 kB
datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot135.jpg DELETED

Git LFS Details

  • SHA256: 5489c853477a1e0eab0dc7862261e5ff3bca8b18e0dc742fe8be04473e993bb2
  • Pointer size: 130 Bytes
  • Size of remote file: 82.3 kB
datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot180.jpg DELETED

Git LFS Details

  • SHA256: 2991086430d5880b01250023617c255dc165e14a00f199706445132ad7f3501e
  • Pointer size: 130 Bytes
  • Size of remote file: 79.4 kB
datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot225.jpg DELETED

Git LFS Details

  • SHA256: dcca8fbd0b68c41fa987982e56fee055997c09e6e88f7de8d46034a8683c931e
  • Pointer size: 130 Bytes
  • Size of remote file: 81.9 kB
datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot270.jpg DELETED

Git LFS Details

  • SHA256: fbd874af9c4d1406a5adfa10f9454b1444d550dbe21bd57619df906a66e79571
  • Pointer size: 130 Bytes
  • Size of remote file: 66.5 kB
datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot315.jpg DELETED

Git LFS Details

  • SHA256: e6fc41beb78ec8e5adef2e601a344cddfe5fe353b4893e186b6036452f8c8198
  • Pointer size: 130 Bytes
  • Size of remote file: 82 kB
datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot45.jpg DELETED

Git LFS Details

  • SHA256: cc55ac4f079176709f577564d390bf3e2c4e08f53378270465062d198699c100
  • Pointer size: 130 Bytes
  • Size of remote file: 81.7 kB
datasets/sacre_coeur/mapping_rot/03903474_1471484089_rot90.jpg DELETED

Git LFS Details

  • SHA256: f5ef044c01f3e94868480cab60841caa92a410c33643d9af6b60be14ee37d60f
  • Pointer size: 130 Bytes
  • Size of remote file: 66.6 kB
datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot135.jpg DELETED

Git LFS Details

  • SHA256: 7b5cd34c3b6ff6fed9c32fe94e9311d9fcd94e4b6ed23ff205fca44c570e1827
  • Pointer size: 130 Bytes
  • Size of remote file: 96.7 kB
datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot180.jpg DELETED

Git LFS Details

  • SHA256: 225c06d54c04d2ed14ec6d885a91760e9faaf298e0a42e776603d44759736b30
  • Pointer size: 131 Bytes
  • Size of remote file: 104 kB
datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot225.jpg DELETED

Git LFS Details

  • SHA256: 9bda2da8868f114170621d93595d1b4600dff8ddda6e8211842b5598ac866ed3
  • Pointer size: 131 Bytes
  • Size of remote file: 101 kB
datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot270.jpg DELETED

Git LFS Details

  • SHA256: de7d6445cadee22083af4a99b970674ee2eb4286162ec65e9e134cb29d1b2748
  • Pointer size: 130 Bytes
  • Size of remote file: 83.1 kB
datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot315.jpg DELETED

Git LFS Details

  • SHA256: f14f08b0d72e7c3d946a50df958db3aa4058d1f9e5acb3ebb3d39a53503b1126
  • Pointer size: 130 Bytes
  • Size of remote file: 96.8 kB
datasets/sacre_coeur/mapping_rot/10265353_3838484249_rot45.jpg DELETED

Git LFS Details

  • SHA256: b5ec4c6fa41d84c07c8fffad7f19630fe2ddb88b042e1a80b470a3566320cb77
  • Pointer size: 131 Bytes
  • Size of remote file: 102 kB