-
Notifications
You must be signed in to change notification settings - Fork 80
/
dogaffnethardnet.py
105 lines (94 loc) · 3.72 KB
/
dogaffnethardnet.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
from argparse import Namespace
import torch
import numpy as np
import cv2
import kornia as K
import kornia.feature as KF
from kornia_moons.feature import laf_from_opencv_SIFT_kpts
from .base import FeatureDetection, Matching
from ..utils.data_io import read_im_gray
class DogAffNetHardNet(FeatureDetection, Matching):
def __init__(self, args=None):
super().__init__()
if type(args) == dict:
args = Namespace(**args)
self.imsize = args.imsize
try:
self.device = args.device
except:
self.device = torch.device("cpu")
self.match_threshold = args.match_threshold
self.det = cv2.SIFT_create(
args.npts, contrastThreshold=-10000, edgeThreshold=-10000
)
self.desc = KF.HardNet(True).eval().to(self.device)
self.aff = KF.LAFAffNetShapeEstimator(True).eval().to(self.device)
self.name = f"DoG{args.npts}-AffNet-HardNet"
print(f"Initialize {self.name}")
def load_im(self, im_path):
im, scale = read_im_gray(im_path, self.imsize)
im = np.array(im)
return im, scale
def load_and_extract(self, im_path):
im, scale = self.load_im(im_path)
kpts, desc = self.extract_features(im)
kpts = kpts * scale
return kpts, desc
def extract_features(self, im):
kpts = self.det.detect(im, None)
# We will not train anything, so let's save time and memory by no_grad()
with torch.no_grad():
timg = K.image_to_tensor(im, False).float() / 255.0
timg = timg.to(self.device)
if timg.shape[1] == 3:
timg_gray = K.rgb_to_grayscale(timg)
else:
timg_gray = timg
# kornia expects keypoints in the local affine frame format.
# Luckily, kornia_moons has a conversion function
lafs = laf_from_opencv_SIFT_kpts(kpts, device=self.device)
lafs_new = self.aff(lafs, timg_gray)
patches = KF.extract_patches_from_pyramid(timg_gray, lafs_new, 32)
B, N, CH, H, W = patches.size()
# Descriptor accepts standard tensor [B, CH, H, W], while patches are [B, N, CH, H, W] shape
# So we need to reshape a bit :)
descs = (
self.desc(patches.view(B * N, CH, H, W))
.view(B * N, -1)
.detach()
.cpu()
.numpy()
)
kpts = np.array([[kp.pt[0], kp.pt[1]] for kp in kpts])
return kpts, descs
def load_and_detect(self, im_path):
im, scale = self.load_im(im_path)
kpts, desc = self.detect(im)
kpts = kpts * scale
return kpts
def detect(self, im):
kpts = self.det.detect(im)
kpts = np.array([[kp.pt[0], kp.pt[1]] for kp in kpts])
return kpts
def match_inputs_(self, im1, im2):
kpts1, desc1 = self.extract_features(im1)
kpts2, desc2 = self.extract_features(im2)
# NN Match
dists, match_ids = KF.match_smnn(
torch.from_numpy(desc1), torch.from_numpy(desc2), self.match_threshold
)
match_ids = match_ids.data.numpy()
p1s = kpts1[match_ids[:, 0], :2]
p2s = kpts2[match_ids[:, 1], :2]
matches = np.concatenate([p1s, p2s], axis=1)
scores = 1.0 - dists
return matches, kpts1, kpts2, scores
def match_pairs(self, im1_path, im2_path):
im1, sc1 = self.load_im(im1_path)
im2, sc2 = self.load_im(im2_path)
upscale = np.array([sc1 + sc2])
matches, kpts1, kpts2, scores = self.match_inputs_(im1, im2)
matches = upscale * matches
kpts1 = sc1 * kpts1
kpts2 = sc2 * kpts2
return matches, kpts1, kpts2, scores