essential_mat_reconstr.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import numpy as np, cv2 as cv, matplotlib.pyplot as plt, time, sys, os
  2. from mpl_toolkits.mplot3d import axes3d, Axes3D
  3. def getEpipolarError(F, pts1_, pts2_, inliers):
  4. pts1 = np.concatenate((pts1_.T, np.ones((1, pts1_.shape[0]))))[:,inliers]
  5. pts2 = np.concatenate((pts2_.T, np.ones((1, pts2_.shape[0]))))[:,inliers]
  6. lines2 = np.dot(F , pts1)
  7. lines1 = np.dot(F.T, pts2)
  8. return np.median((np.abs(np.sum(pts1 * lines1, axis=0)) / np.sqrt(lines1[0,:]**2 + lines1[1,:]**2) +
  9. np.abs(np.sum(pts2 * lines2, axis=0)) / np.sqrt(lines2[0,:]**2 + lines2[1,:]**2))/2)
  10. if __name__ == '__main__':
  11. if len(sys.argv) < 3:
  12. print("Path to data file and directory to image files are missing!\nData file must have"
  13. " format:\n--------------\n image_name_1\nimage_name_2\nk11 k12 k13\n0 k22 k23\n"
  14. "0 0 1\n--------------\nIf image_name_{1,2} are not in the same directory as "
  15. "the data file then add argument with directory to image files.\nFor example: "
  16. "python essential_mat_reconstr.py essential_mat_data.txt ./")
  17. exit(1)
  18. else:
  19. data_file = sys.argv[1]
  20. image_dir = sys.argv[2]
  21. if not os.path.isfile(data_file):
  22. print('Incorrect path to data file!')
  23. exit(1)
  24. with open(data_file, 'r') as f:
  25. image1 = cv.imread(image_dir+f.readline()[:-1]) # remove '\n'
  26. image2 = cv.imread(image_dir+f.readline()[:-1])
  27. K = np.array([[float(x) for x in f.readline().split(' ')],
  28. [float(x) for x in f.readline().split(' ')],
  29. [float(x) for x in f.readline().split(' ')]])
  30. if image1 is None or image2 is None:
  31. print('Incorrect directory to images!')
  32. exit(1)
  33. if K.shape != (3,3):
  34. print('Intrinsic matrix has incorrect format!')
  35. exit(1)
  36. print('find keypoints and compute descriptors')
  37. detector = cv.SIFT_create(nfeatures=20000)
  38. keypoints1, descriptors1 = detector.detectAndCompute(cv.cvtColor(image1, cv.COLOR_BGR2GRAY), None)
  39. keypoints2, descriptors2 = detector.detectAndCompute(cv.cvtColor(image2, cv.COLOR_BGR2GRAY), None)
  40. matcher = cv.FlannBasedMatcher(dict(algorithm=0, trees=5), dict(checks=32))
  41. print('match with FLANN, size of descriptors', descriptors1.shape, descriptors2.shape)
  42. matches_vector = matcher.knnMatch(descriptors1, descriptors2, k=2)
  43. print('find good keypoints')
  44. pts1 = []; pts2 = []
  45. for m in matches_vector:
  46. # compare best and second match using Lowe ratio test
  47. if m[0].distance / m[1].distance < 0.75:
  48. pts1.append(keypoints1[m[0].queryIdx].pt)
  49. pts2.append(keypoints2[m[0].trainIdx].pt)
  50. pts1 = np.array(pts1); pts2 = np.array(pts2)
  51. print('points size', pts1.shape[0])
  52. print('Essential matrix RANSAC')
  53. start = time.time()
  54. E, inliers = cv.findEssentialMat(pts1, pts2, K, cv.RANSAC, 0.999, 1.0)
  55. print('RANSAC time', time.time() - start, 'seconds')
  56. print('Median error to epipolar lines', getEpipolarError
  57. (np.dot(np.linalg.inv(K).T, np.dot(E, np.linalg.inv(K))), pts1, pts2, inliers.squeeze()),
  58. 'number of inliers', inliers.sum())
  59. print('Decompose essential matrix')
  60. R1, R2, t = cv.decomposeEssentialMat(E)
  61. # Assume relative pose. Fix the first camera
  62. P1 = np.concatenate((K, np.zeros((3,1))), axis=1) # K [I | 0]
  63. P2s = [np.dot(K, np.concatenate((R1, t), axis=1)), # K[R1 | t]
  64. np.dot(K, np.concatenate((R1, -t), axis=1)), # K[R1 | -t]
  65. np.dot(K, np.concatenate((R2, t), axis=1)), # K[R2 | t]
  66. np.dot(K, np.concatenate((R2, -t), axis=1))] # K[R2 | -t]
  67. obj_pts_per_cam = []
  68. # enumerate over all P2 projection matrices
  69. for cam_idx, P2 in enumerate(P2s):
  70. obj_pts = []
  71. for i, (pt1, pt2) in enumerate(zip(pts1, pts2)):
  72. if not inliers[i]:
  73. continue
  74. # find object point by triangulation of image points by projection matrices
  75. obj_pt = cv.triangulatePoints(P1, P2, pt1, pt2)
  76. obj_pt /= obj_pt[3]
  77. # check if reprojected point has positive depth
  78. if obj_pt[2] > 0:
  79. obj_pts.append([obj_pt[0], obj_pt[1], obj_pt[2]])
  80. obj_pts_per_cam.append(obj_pts)
  81. best_cam_idx = np.array([len(obj_pts_per_cam[0]),len(obj_pts_per_cam[1]),
  82. len(obj_pts_per_cam[2]),len(obj_pts_per_cam[3])]).argmax()
  83. max_pts = len(obj_pts_per_cam[best_cam_idx])
  84. print('Number of object points', max_pts)
  85. # filter object points to have reasonable depth
  86. MAX_DEPTH = 6.
  87. obj_pts = []
  88. for pt in obj_pts_per_cam[best_cam_idx]:
  89. if pt[2] < MAX_DEPTH:
  90. obj_pts.append(pt)
  91. obj_pts = np.array(obj_pts).reshape(len(obj_pts), 3)
  92. # visualize image points
  93. for i, (pt1, pt2) in enumerate(zip(pts1, pts2)):
  94. if inliers[i]:
  95. cv.circle(image1, (int(pt1[0]), int(pt1[1])), 7, (255,0,0), -1)
  96. cv.circle(image2, (int(pt2[0]), int(pt2[1])), 7, (255,0,0), -1)
  97. # concatenate two images
  98. image1 = np.concatenate((image1, image2), axis=1)
  99. # resize concatenated image
  100. new_img_size = 1200. * 800.
  101. image1 = cv.resize(image1, (int(np.sqrt(image1.shape[1] * new_img_size / image1.shape[0])),
  102. int(np.sqrt (image1.shape[0] * new_img_size / image1.shape[1]))))
  103. # plot object points
  104. fig = plt.figure(figsize=(13.0, 11.0))
  105. ax = fig.add_subplot(111, projection='3d')
  106. ax.set_aspect('equal')
  107. ax.scatter(obj_pts[:,0], obj_pts[:,1], obj_pts[:,2], c='r', marker='o', s=3)
  108. ax.set_xlabel('x')
  109. ax.set_ylabel('y')
  110. ax.set_zlabel('depth')
  111. ax.view_init(azim=-80, elev=110)
  112. # save figures
  113. cv.imshow("matches", image1)
  114. cv.imwrite('matches_E.png', image1)
  115. plt.savefig('reconstruction_3D.png')
  116. cv.waitKey(0)
  117. plt.show()