Skip to content

Commit 8f9c1d4

Browse files
Phil26ATskydessarlinpe
authored
Cleanup matcher and simplify interface (cvg#22)
--------- Co-authored-by: Paul-Edouard Sarlin <[email protected]> Co-authored-by: Paul-Edouard Sarlin <[email protected]>
1 parent c3c94be commit 8f9c1d4

File tree

9 files changed

+379
-220
lines changed

9 files changed

+379
-220
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.ipynb linguist-documentation

README.md

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<p align="center">
2-
<h1 align="center"><ins>LightGlue</ins><br>Local Feature Matching at Light Speed</h1>
2+
<h1 align="center"><ins>LightGlue ⚡️</ins><br>Local Feature Matching at Light Speed</h1>
33
<p align="center">
44
<a href="https://www.linkedin.com/in/philipplindenberger/">Philipp Lindenberger</a>
55
·
@@ -11,26 +11,28 @@
1111
<img src="assets/larchitecture.svg" alt="Logo" height="40">
1212
</p> -->
1313
<!-- <h2 align="center">PrePrint 2023</h2> -->
14-
<h2><p align="center"><a href="https://arxiv.org/pdf/2306.13643.pdf" align="center">Paper</a></p></h2>
14+
<h2 align="center"><p>
15+
<a href="https://arxiv.org/pdf/2306.13643.pdf" align="center">Paper</a> |
16+
<a href="https://colab.research.google.com/github/cvg/LightGlue/blob/main/demo.ipynb" align="center">Colab</a>
17+
</p></h2>
1518
<div align="center"></div>
1619
</p>
1720
<p align="center">
18-
<a href="https://arxiv.org/abs/2306.13643"><img src="assets/easy_hard.jpg" alt="Logo" width=80%></a>
21+
<a href="https://arxiv.org/abs/2306.13643"><img src="assets/easy_hard.jpg" alt="example" width=80%></a>
1922
<br>
20-
<em>LightGlue is a Graph Neural Network for local feature matching that introspects its confidences to 1) stop early if all predictions are ready and 2) remove points deemed unmatchable to save compute.</em>
23+
<em>LightGlue is a deep neural network that matches sparse local features across image pairs.<br>An adaptive mechanism makes it fast for easy pairs (top) and reduces the computational complexity for difficult ones (bottom).</em>
2124
</p>
2225

2326
##
2427

25-
This repository hosts the inference code for LightGlue, a lightweight feature matcher with high accuracy and adaptive pruning techniques, both in the width and depth of the network, for blazing fast inference. It takes as input a set of keypoints and descriptors for each image, and returns the indices of corresponding points between them.
28+
This repository hosts the inference code of LightGlue, a lightweight feature matcher with high accuracy and blazing fast inference. It takes as input a set of keypoints and descriptors for each image and returns the indices of corresponding points. The architecture is based on adaptive pruning techniques, in both network width and depth - [check out the paper for more details](https://arxiv.org/pdf/2306.13643.pdf).
2629

2730
We release pretrained weights of LightGlue with [SuperPoint](https://arxiv.org/abs/1712.07629) and [DISK](https://arxiv.org/abs/2006.13566) local features.
31+
The training end evaluation code will be released in July in a separate repo. To be notified, subscribe to [issue #6](https://github.com/cvg/LightGlue/issues/6).
2832

29-
The training end evaluation code will be released in July in a separate repo. If you wish to be notified, subscribe to [Issue #6](https://github.com/cvg/LightGlue/issues/6).
33+
## Installation and demo [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/cvg/LightGlue/blob/main/demo.ipynb)
3034

31-
## Installation and Demo
32-
33-
You can install this repo pip:
35+
Install this repo using pip:
3436

3537
```bash
3638
git clone https://github.com/cvg/LightGlue.git && cd LightGlue
@@ -43,42 +45,77 @@ Here is a minimal script to match two images:
4345

4446
```python
4547
from lightglue import LightGlue, SuperPoint, DISK
46-
from lightglue.utils import load_image, match_pair
48+
from lightglue.utils import load_image, rbd
4749

4850
# SuperPoint+LightGlue
4951
extractor = SuperPoint(max_num_keypoints=2048).eval().cuda() # load the extractor
50-
matcher = LightGlue(pretrained='superpoint').eval().cuda() # load the matcher
52+
matcher = LightGlue(features='superpoint').eval().cuda() # load the matcher
5153

5254
# or DISK+LightGlue
5355
extractor = DISK(max_num_keypoints=2048).eval().cuda() # load the extractor
54-
matcher = LightGlue(pretrained='disk').eval().cuda() # load the matcher
55-
56-
# load images to torch and resize to max_edge=1024
57-
image0, scales0 = load_image(path_to_image_0, resize=1024)
58-
image1, scales1 = load_image(path_to_image_1, resize=1024)
56+
matcher = LightGlue(features='disk').eval().cuda() # load the matcher
57+
58+
# load each image as a torch.Tensor on GPU with shape (3,H,W), normalized in [0,1]
59+
image0 = load_image('path/to/image_0.jpg').cuda()
60+
image1 = load_image('path/to/image_1.jpg').cuda()
61+
62+
# extract local features
63+
feats0 = extractor.extract(image0) # auto-resize the image, disable with resize=None
64+
feats1 = extractor.extract(image1)
65+
66+
# match the features
67+
matches01 = matcher({'image0': feats0, 'image1': feats1})
68+
feats0, feats1, matches01 = [rbd(x) for x in [feats0, feats1, matches01]] # remove batch dimension
69+
matches = matches01['matches'] # indices with shape (K,2)
70+
points0 = feats0['keypoints'][matches[..., 0]] # coordinates in image #0, shape (K,2)
71+
points1 = feats1['keypoints'][matches[..., 1]] # coordinates in image #1, shape (K,2)
72+
```
5973

60-
# extraction + matching + rescale keypoints to original image size
61-
pred = match_pair(extractor, matcher, image0, image1,
62-
scales0=scales0, scales1=scales1)
74+
We also provide a convenience method to match a pair of images:
6375

64-
kpts0, kpts1, matches = pred['keypoints0'], pred['keypoints1'], pred['matches']
65-
m_kpts0, m_kpts1 = kpts0[matches[..., 0]], kpts1[matches[..., 1]]
76+
```python
77+
from lightglue import match_pair
78+
feats0, feats1, matches01 = match_pair(extractor, matcher, image0, image1)
6679
```
6780

68-
## Tradeoff Speed vs. Accuracy
69-
LightGlue can adjust its depth (number of layers) and width (number of keypoints) per image pair, with a minimal impact on accuracy.
81+
##
82+
7083
<p align="center">
7184
<a href="https://arxiv.org/abs/2306.13643"><img src="assets/teaser.svg" alt="Logo" width=50%></a>
85+
<br>
86+
<em>LightGlue can adjust its depth (number of layers) and width (number of keypoints) per image pair, with a marginal impact on accuracy.</em>
7287
</p>
7388

74-
- [```depth_confidence```](https://github.com/cvg/LightGlue/blob/release/lightglue/lightglue.py#L265): Controls early stopping, improves run time. Recommended: 0.95. Default: -1 (off)
75-
- [```width_confidence```](https://github.com/cvg/LightGlue/blob/release/lightglue/lightglue.py#L266): Controls iterative feature removal, improves run time. Recommended: 0.99. Default: -1 (off)
76-
- [```flash```](https://github.com/cvg/LightGlue/blob/release/lightglue/lightglue.py#L262): Enable [FlashAttention](https://github.com/HazyResearch/flash-attention/tree/main). Significantly improves runtime and reduces memory consumption without any impact on accuracy, but requires either [FlashAttention](https://github.com/HazyResearch/flash-attention/tree/main) or ```torch >= 2.0```.
89+
## Advanced configuration
90+
91+
The default values give a good trade-off between speed and accuracy. To maximize the accuracy, use all keypoints and disable the adaptive mechanisms:
92+
```python
93+
extractor = SuperPoint(max_num_keypoints=None)
94+
matcher = LightGlue(features='superpoint', depth_confidence=-1, width_confidence=-1)
95+
```
96+
97+
To increase the speed with a small drop of accuracy, decrease the number of keypoints and lower the adaptive thresholds:
98+
```python
99+
extractor = SuperPoint(max_num_keypoints=1024)
100+
matcher = LightGlue(features='superpoint', depth_confidence=0.9, width_confidence=0.95)
101+
```
102+
The maximum speed is obtained with [FlashAttention](https://arxiv.org/abs/2205.14135), which is automatically used when ```torch >= 2.0``` or if it is [installed from source](https://github.com/HazyResearch/flash-attention#installation-and-features).
103+
104+
<details>
105+
<summary>[Detail of all parameters - click to expand]</summary>
77106

107+
- [```n_layers```](https://github.com/cvg/LightGlue/blob/main/lightglue/lightglue.py#L261): Number of stacked self+cross attention layers. Reduce this value for faster inference at the cost of accuracy (continuous red line in the plot above). Default: 9 (all layers).
108+
- [```flash```](https://github.com/cvg/LightGlue/blob/main/lightglue/lightglue.py#L263): Enable FlashAttention. Significantly increases the speed and reduces the memory consumption without any impact on accuracy. Default: True (LightGlue automatically detects if FlashAttention is available).
109+
- [```mp```](https://github.com/cvg/LightGlue/blob/main/lightglue/lightglue.py#L264): Enable mixed precision inference. Default: False (off)
110+
- [```depth_confidence```](https://github.com/cvg/LightGlue/blob/main/lightglue/lightglue.py#L265): Controls the early stopping. A lower values stops more often at earlier layers. Default: 0.95, disable with -1.
111+
- [```width_confidence```](https://github.com/cvg/LightGlue/blob/main/lightglue/lightglue.py#L266): Controls the iterative point pruning. A lower value prunes more points earlier. Default: 0.99, disable with -1.
112+
- [```filter_threshold```](https://github.com/cvg/LightGlue/blob/main/lightglue/lightglue.py#L267): Match confidence. Increase this value to obtain less, but stronger matches. Default: 0.1
78113

79-
## LightGlue in other frameworks
80-
- ONNX: [fabio-sim](https://github.com/fabio-sim) was blazing fast in implementing an ONNX-compatible version of LightGlue [here](https://github.com/fabio-sim/LightGlue-ONNX).
114+
</details>
81115

116+
## Other links
117+
- [LightGlue-ONNX](https://github.com/fabio-sim/LightGlue-ONNX): export LightGlue to the Open Neural Network Exchange format.
118+
- [Image Matching WebUI](https://github.com/Vincentqyw/image-matching-webui): a web GUI to easily compare different matchers, including LightGlue.
82119

83120
## BibTeX Citation
84121
If you use any ideas from the paper or code from this repo, please consider citing:

demo.ipynb

Lines changed: 55 additions & 55 deletions
Large diffs are not rendered by default.

lightglue/disk.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import torch.nn as nn
33
import kornia
44
from types import SimpleNamespace
5+
from .utils import ImagePreprocessor
56

67

78
class DISK(nn.Module):
@@ -13,6 +14,13 @@ class DISK(nn.Module):
1314
'detection_threshold': 0.0,
1415
'pad_if_not_divisible': True,
1516
}
17+
18+
preprocess_conf = {
19+
**ImagePreprocessor.default_conf,
20+
'resize': 1024,
21+
'grayscale': False,
22+
}
23+
1624
required_data_keys = ['image']
1725

1826
def __init__(self, **conf) -> None:
@@ -22,8 +30,10 @@ def __init__(self, **conf) -> None:
2230
self.model = kornia.feature.DISK.from_pretrained(self.conf.weights)
2331

2432
def forward(self, data: dict) -> dict:
33+
""" Compute keypoints, scores, descriptors for image """
34+
for key in self.required_data_keys:
35+
assert key in data, f'Missing key {key} in data'
2536
image = data['image']
26-
2737
features = self.model(
2838
image,
2939
n=self.conf.max_num_keypoints,
@@ -45,3 +55,16 @@ def forward(self, data: dict) -> dict:
4555
'keypoint_scores': scores.to(image),
4656
'descriptors': descriptors.to(image),
4757
}
58+
59+
def extract(self, img: torch.Tensor, **conf) -> dict:
60+
""" Perform extraction with online resizing"""
61+
if img.dim() == 3:
62+
img = img[None] # add batch dim
63+
assert img.dim() == 4 and img.shape[0] == 1
64+
shape = img.shape[-2:][::-1]
65+
img, scales = ImagePreprocessor(
66+
**{**self.preprocess_conf, **conf})(img)
67+
feats = self.forward({'image': img})
68+
feats['image_size'] = torch.tensor(shape)[None].to(img).float()
69+
feats['keypoints'] = (feats['keypoints'] + .5) / scales[None] - .5
70+
return feats

0 commit comments

Comments
 (0)