-
Notifications
You must be signed in to change notification settings - Fork 337
/
feature_aliked.py
126 lines (104 loc) · 5.24 KB
/
feature_aliked.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""
* This file is part of PYSLAM
* Adapted from https://github.com/cvlab-epfl/disk/blob/master/detect.py, see licence therein.
*
* Copyright (C) 2016-present Luigi Freda <luigi dot freda at gmail dot com>
*
* PYSLAM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PYSLAM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PYSLAM. If not, see <http://www.gnu.org/licenses/>.
"""
# adapted from https://github.com/cvlab-epfl/disk/blob/master/detect.py
import sys
import config
config.cfg.set_lib('lightglue')
import numpy as np
import torch
import cv2
from threading import RLock
from utils_sys import Printer, import_from, is_opencv_version_greater_equal
ALIKED = import_from('lightglue', 'ALIKED')
kVerbose = True
def numpy_image_to_torch(image: np.ndarray) -> torch.Tensor:
"""Normalize the image tensor and reorder the dimensions."""
if image.ndim == 3:
image = image.transpose((2, 0, 1)) # HxWxC to CxHxW
elif image.ndim == 2:
image = image[None] # add channel axis
else:
raise ValueError(f"Not an image: {image.shape}")
return torch.tensor(image / 255.0, dtype=torch.float)
# convert matrix of pts into list of keypoints
def convert_pts_to_keypoints(pts, size):
kps = []
if pts is not None:
# convert matrix [Nx2] of pts into list of keypoints
if is_opencv_version_greater_equal(4,5,3):
kps = [ cv2.KeyPoint(p[0], p[1], size=size, response=1.0, octave=0) for p in pts ]
else:
kps = [ cv2.KeyPoint(p[0], p[1], _size=size, _response=1.0, _octave=0) for p in pts ]
return kps
# interface for pySLAM
# NOTE: from Fig. 3 in the paper "ALIKED: Learning local features with policy gradient"
# "Our approach can match many more points and produce more accurate poses. It can deal with large changes in scale (4th and 5th columns) but not in rotation..."
class AlikedFeature2D:
def __init__(self,
num_features=2000):
print('Using AlikedFeature2D')
self.lock = RLock()
self.num_features = num_features
config = ALIKED.default_conf.copy()
config['max_num_keypoints'] = self.num_features
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 'mps', 'cpu'
self.ALIKED = ALIKED(conf=config).eval().to(self.device)
def setMaxFeatures(self, num_features): # use the cv2 method name for extractors (see https://docs.opencv.org/4.x/db/d95/classcv_1_1ORB.html#aca471cb82c03b14d3e824e4dcccf90b7)
self.num_features = num_features
def extract(self, image):
tensor = numpy_image_to_torch(image)
feats = self.ALIKED.extract(tensor.to(self.device))
#print(f'feats: {feats}')
kps = feats["keypoints"].cpu().numpy()[0]
des = feats["descriptors"].cpu().numpy()[0]
return kps, des
def compute_kps_des(self, im):
with self.lock:
keypoints, descriptors = self.extract(im)
#print('keypoints: ', keypoints)
self.kps = convert_pts_to_keypoints(keypoints, size = 1)
return self.kps, descriptors
def detectAndCompute(self, frame, mask=None): #mask is a fake input
with self.lock:
self.frame = frame
self.kps, self.des = self.compute_kps_des(frame)
if kVerbose:
print('detector: ALIKED, descriptor: ALIKED, #features: ', len(self.kps), ', frame res: ', frame.shape[0:2])
return self.kps, self.des
# return keypoints if available otherwise call detectAndCompute()
def detect(self, frame, mask=None): # mask is a fake input
with self.lock:
#if self.frame is not frame:
self.detectAndCompute(frame)
return self.kps
# return descriptors if available otherwise call detectAndCompute()
def compute(self, frame, kps=None, mask=None): # kps is a fake input, mask is a fake input
with self.lock:
if self.frame is not frame:
Printer.orange('WARNING: ALIKED is recomputing both kps and des on last input frame', frame.shape)
self.detectAndCompute(frame)
return self.kps, self.des
# return descriptors if available otherwise call detectAndCompute()
def compute(self, frame, kps=None, mask=None): # kps is a fake input, mask is a fake input
with self.lock:
if self.frame is not frame:
#Printer.orange('WARNING: ALIKED is recomputing both kps and des on last input frame', frame.shape)
self.detectAndCompute(frame)
return self.kps, self.des