essential_mat_reconstr.py
5.8 KB
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
127
128
129
130
131
132
133
134
135
136
137
import numpy as np, cv2 as cv, matplotlib.pyplot as plt, time, sys, os
from mpl_toolkits.mplot3d import axes3d, Axes3D
def getEpipolarError(F, pts1_, pts2_, inliers):
pts1 = np.concatenate((pts1_.T, np.ones((1, pts1_.shape[0]))))[:,inliers]
pts2 = np.concatenate((pts2_.T, np.ones((1, pts2_.shape[0]))))[:,inliers]
lines2 = np.dot(F , pts1)
lines1 = np.dot(F.T, pts2)
return np.median((np.abs(np.sum(pts1 * lines1, axis=0)) / np.sqrt(lines1[0,:]**2 + lines1[1,:]**2) +
np.abs(np.sum(pts2 * lines2, axis=0)) / np.sqrt(lines2[0,:]**2 + lines2[1,:]**2))/2)
if __name__ == '__main__':
if len(sys.argv) < 3:
print("Path to data file and directory to image files are missing!\nData file must have"
" format:\n--------------\n image_name_1\nimage_name_2\nk11 k12 k13\n0 k22 k23\n"
"0 0 1\n--------------\nIf image_name_{1,2} are not in the same directory as "
"the data file then add argument with directory to image files.\nFor example: "
"python essential_mat_reconstr.py essential_mat_data.txt ./")
exit(1)
else:
data_file = sys.argv[1]
image_dir = sys.argv[2]
if not os.path.isfile(data_file):
print('Incorrect path to data file!')
exit(1)
with open(data_file, 'r') as f:
image1 = cv.imread(image_dir+f.readline()[:-1]) # remove '\n'
image2 = cv.imread(image_dir+f.readline()[:-1])
K = np.array([[float(x) for x in f.readline().split(' ')],
[float(x) for x in f.readline().split(' ')],
[float(x) for x in f.readline().split(' ')]])
if image1 is None or image2 is None:
print('Incorrect directory to images!')
exit(1)
if K.shape != (3,3):
print('Intrinsic matrix has incorrect format!')
exit(1)
print('find keypoints and compute descriptors')
detector = cv.SIFT_create(nfeatures=20000)
keypoints1, descriptors1 = detector.detectAndCompute(cv.cvtColor(image1, cv.COLOR_BGR2GRAY), None)
keypoints2, descriptors2 = detector.detectAndCompute(cv.cvtColor(image2, cv.COLOR_BGR2GRAY), None)
matcher = cv.FlannBasedMatcher(dict(algorithm=0, trees=5), dict(checks=32))
print('match with FLANN, size of descriptors', descriptors1.shape, descriptors2.shape)
matches_vector = matcher.knnMatch(descriptors1, descriptors2, k=2)
print('find good keypoints')
pts1 = []; pts2 = []
for m in matches_vector:
# compare best and second match using Lowe ratio test
if m[0].distance / m[1].distance < 0.75:
pts1.append(keypoints1[m[0].queryIdx].pt)
pts2.append(keypoints2[m[0].trainIdx].pt)
pts1 = np.array(pts1); pts2 = np.array(pts2)
print('points size', pts1.shape[0])
print('Essential matrix RANSAC')
start = time.time()
E, inliers = cv.findEssentialMat(pts1, pts2, K, cv.RANSAC, 0.999, 1.0)
print('RANSAC time', time.time() - start, 'seconds')
print('Median error to epipolar lines', getEpipolarError
(np.dot(np.linalg.inv(K).T, np.dot(E, np.linalg.inv(K))), pts1, pts2, inliers.squeeze()),
'number of inliers', inliers.sum())
print('Decompose essential matrix')
R1, R2, t = cv.decomposeEssentialMat(E)
# Assume relative pose. Fix the first camera
P1 = np.concatenate((K, np.zeros((3,1))), axis=1) # K [I | 0]
P2s = [np.dot(K, np.concatenate((R1, t), axis=1)), # K[R1 | t]
np.dot(K, np.concatenate((R1, -t), axis=1)), # K[R1 | -t]
np.dot(K, np.concatenate((R2, t), axis=1)), # K[R2 | t]
np.dot(K, np.concatenate((R2, -t), axis=1))] # K[R2 | -t]
obj_pts_per_cam = []
# enumerate over all P2 projection matrices
for cam_idx, P2 in enumerate(P2s):
obj_pts = []
for i, (pt1, pt2) in enumerate(zip(pts1, pts2)):
if not inliers[i]:
continue
# find object point by triangulation of image points by projection matrices
obj_pt = cv.triangulatePoints(P1, P2, pt1, pt2)
obj_pt /= obj_pt[3]
# check if reprojected point has positive depth
if obj_pt[2] > 0:
obj_pts.append([obj_pt[0], obj_pt[1], obj_pt[2]])
obj_pts_per_cam.append(obj_pts)
best_cam_idx = np.array([len(obj_pts_per_cam[0]),len(obj_pts_per_cam[1]),
len(obj_pts_per_cam[2]),len(obj_pts_per_cam[3])]).argmax()
max_pts = len(obj_pts_per_cam[best_cam_idx])
print('Number of object points', max_pts)
# filter object points to have reasonable depth
MAX_DEPTH = 6.
obj_pts = []
for pt in obj_pts_per_cam[best_cam_idx]:
if pt[2] < MAX_DEPTH:
obj_pts.append(pt)
obj_pts = np.array(obj_pts).reshape(len(obj_pts), 3)
# visualize image points
for i, (pt1, pt2) in enumerate(zip(pts1, pts2)):
if inliers[i]:
cv.circle(image1, (int(pt1[0]), int(pt1[1])), 7, (255,0,0), -1)
cv.circle(image2, (int(pt2[0]), int(pt2[1])), 7, (255,0,0), -1)
# concatenate two images
image1 = np.concatenate((image1, image2), axis=1)
# resize concatenated image
new_img_size = 1200. * 800.
image1 = cv.resize(image1, (int(np.sqrt(image1.shape[1] * new_img_size / image1.shape[0])),
int(np.sqrt (image1.shape[0] * new_img_size / image1.shape[1]))))
# plot object points
fig = plt.figure(figsize=(13.0, 11.0))
ax = fig.add_subplot(111, projection='3d')
ax.set_aspect('equal')
ax.scatter(obj_pts[:,0], obj_pts[:,1], obj_pts[:,2], c='r', marker='o', s=3)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('depth')
ax.view_init(azim=-80, elev=110)
# save figures
cv.imshow("matches", image1)
cv.imwrite('matches_E.png', image1)
plt.savefig('reconstruction_3D.png')
cv.waitKey(0)
plt.show()