forked from geohot/twitchslam
-
Notifications
You must be signed in to change notification settings - Fork 0
/
helpers.py
117 lines (96 loc) · 3.54 KB
/
helpers.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
import os
import numpy as np
# colors shamelessly stolen from
# https://github.com/MagicLeapResearch/SuperPointPretrainedNetwork/blob/master/demo_superpoint.py
myjet = np.array([[0. , 0. , 0.5 ],
[0. , 0. , 0.99910873],
[0. , 0.37843137, 1. ],
[0. , 0.83333333, 1. ],
[0.30044276, 1. , 0.66729918],
[0.66729918, 1. , 0.30044276],
[1. , 0.90123457, 0. ],
[1. , 0.48002905, 0. ],
[0.99910873, 0.07334786, 0. ],
[0.5 , 0. , 0. ]])
def hamming_distance(a, b):
r = (1 << np.arange(8))[:,None]
return np.count_nonzero((np.bitwise_xor(a,b) & r) != 0)
def triangulate(pose1, pose2, pts1, pts2):
ret = np.zeros((pts1.shape[0], 4))
for i, p in enumerate(zip(pts1, pts2)):
A = np.zeros((4,4))
A[0] = p[0][0] * pose1[2] - pose1[0]
A[1] = p[0][1] * pose1[2] - pose1[1]
A[2] = p[1][0] * pose2[2] - pose2[0]
A[3] = p[1][1] * pose2[2] - pose2[1]
_, _, vt = np.linalg.svd(A)
ret[i] = vt[3]
return ret
# turn [[x,y]] -> [[x,y,1]]
def add_ones(x):
if len(x.shape) == 1:
return np.concatenate([x,np.array([1.0])], axis=0)
else:
return np.concatenate([x, np.ones((x.shape[0], 1))], axis=1)
def poseRt(R, t):
ret = np.eye(4)
ret[:3, :3] = R
ret[:3, 3] = t
return ret
# pose
def fundamentalToRt(F):
W = np.mat([[0,-1,0],[1,0,0],[0,0,1]],dtype=float)
U,d,Vt = np.linalg.svd(F)
if np.linalg.det(U) < 0:
U *= -1.0
if np.linalg.det(Vt) < 0:
Vt *= -1.0
R = np.dot(np.dot(U, W), Vt)
if np.sum(R.diagonal()) < 0:
R = np.dot(np.dot(U, W.T), Vt)
t = U[:, 2]
# TODO: Resolve ambiguities in better ways. This is wrong.
if t[2] < 0:
t *= -1
# TODO: UGLY!
if os.getenv("REVERSE") is not None:
t *= -1
return np.linalg.inv(poseRt(R, t))
def normalize(Kinv, pts):
return np.dot(Kinv, add_ones(pts).T).T[:, 0:2]
# from https://github.com/scikit-image/scikit-image/blob/master/skimage/transform/_geometric.py
class EssentialMatrixTransform(object):
def __init__(self):
self.params = np.eye(3)
def __call__(self, coords):
coords_homogeneous = np.column_stack([coords, np.ones(coords.shape[0])])
return coords_homogeneous @ self.params.T
def estimate(self, src, dst):
assert src.shape == dst.shape
assert src.shape[0] >= 8
# Setup homogeneous linear equation as dst' * F * src = 0.
A = np.ones((src.shape[0], 9))
A[:, :2] = src
A[:, :3] *= dst[:, 0, np.newaxis]
A[:, 3:5] = src
A[:, 3:6] *= dst[:, 1, np.newaxis]
A[:, 6:8] = src
# Solve for the nullspace of the constraint matrix.
_, _, V = np.linalg.svd(A)
F = V[-1, :].reshape(3, 3)
# Enforcing the internal constraint that two singular values must be
# non-zero and one must be zero.
U, S, V = np.linalg.svd(F)
S[0] = S[1] = (S[0] + S[1]) / 2.0
S[2] = 0
self.params = U @ np.diag(S) @ V
return True
def residuals(self, src, dst):
# Compute the Sampson distance.
src_homogeneous = np.column_stack([src, np.ones(src.shape[0])])
dst_homogeneous = np.column_stack([dst, np.ones(dst.shape[0])])
F_src = self.params @ src_homogeneous.T
Ft_dst = self.params.T @ dst_homogeneous.T
dst_F_src = np.sum(dst_homogeneous * F_src.T, axis=1)
return np.abs(dst_F_src) / np.sqrt(F_src[0] ** 2 + F_src[1] ** 2
+ Ft_dst[0] ** 2 + Ft_dst[1] ** 2)