extractor.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import cv2
  2. import numpy as np
  3. from skimage.measure import ransac
  4. from skimage.transform import FundamentalMatrixTransform
  5. import g2o
  6. def add_ones(x):
  7. # concatenates the original array x with the column of ones along the second axis (columns).
  8. # This converts the N×2 array to an N×3 array where each point is represented
  9. # in homogeneous coordinates as [x,y,1].
  10. return np.concatenate([x, np.ones((x.shape[0], 1))], axis=1)
  11. IRt = np.eye(4)
  12. def extractPose(F):
  13. W = np.mat([[0,-1,0],[1,0,0],[0,0,1]])
  14. U,d,Vt = np.linalg.svd(F)
  15. assert np.linalg.det(U) > 0
  16. if np.linalg.det(Vt) < 0:
  17. Vt *= -1
  18. R = np.dot(np.dot(U, W), Vt)
  19. if np.sum(R.diagonal()) < 0:
  20. R = np.dot(np.dot(U, W.T), Vt)
  21. t = U[:, 2]
  22. ret = np.eye(4)
  23. ret[:3, :3] = R
  24. ret[:3, 3] = t
  25. print(d)
  26. return ret
  27. def extract(img):
  28. orb = cv2.ORB_create()
  29. # Convert to grayscale
  30. gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  31. # Detection
  32. pts = cv2.goodFeaturesToTrack(gray_img, 8000, qualityLevel=0.01, minDistance=10)
  33. if pts is None:
  34. return np.array([]), None
  35. # Extraction
  36. kps = [cv2.KeyPoint(f[0][0], f[0][1], 20) for f in pts]
  37. kps, des = orb.compute(gray_img, kps)
  38. return np.array([(kp.pt[0], kp.pt[1]) for kp in kps]), des
  39. def normalize(Kinv, pts):
  40. # The inverse camera intrinsic matrix 𝐾 − 1 transforms 2D homogeneous points
  41. # from pixel coordinates to normalized image coordinates. This transformation centers
  42. # the points based on the principal point (𝑐𝑥 , 𝑐𝑦) and scales them
  43. # according to the focal lengths 𝑓𝑥 and 𝑓𝑦, effectively mapping the points
  44. # to a normalized coordinate system where the principal point becomes the origin and
  45. # the distances are scaled by the focal lengths.
  46. return np.dot(Kinv, add_ones(pts).T).T[:, 0:2]
  47. # `[:, 0:2]` selects the first two columns of the resulting array, which are the normalized x and y coordinates.
  48. # `.T` transposes the result back to N x 3.
  49. def denormalize(K, pt):
  50. ret = np.dot(K, [pt[0], pt[1], 1.0])
  51. ret /= ret[2]
  52. return int(round(ret[0])), int(round(ret[1]))
  53. class Matcher(object):
  54. def __init__(self):
  55. self.last = None
  56. def match_frames(f1, f2):
  57. bf = cv2.BFMatcher(cv2.NORM_HAMMING)
  58. matches = bf.knnMatch(f1.des, f2.des, k=2)
  59. # Lowe's ratio test
  60. ret = []
  61. idx1, idx2 = [], []
  62. for m, n in matches:
  63. if m.distance < 0.75*n.distance:
  64. p1 = f1.pts[m.queryIdx]
  65. p2 = f2.pts[m.trainIdx]
  66. # Distance test
  67. # dditional distance test, ensuring that the
  68. # Euclidean distance between p1 and p2 is less than 0.1
  69. if np.linalg.norm((p1-p2)) < 0.1:
  70. # Keep idxs
  71. idx1.append(m.queryIdx)
  72. idx2.append(m.trainIdx)
  73. ret.append((p1, p2))
  74. pass
  75. assert len(ret) >= 8
  76. ret = np.array(ret)
  77. idx1 = np.array(idx1)
  78. idx2 = np.array(idx2)
  79. # Fit matrix
  80. model, inliers = ransac((ret[:, 0],
  81. ret[:, 1]), FundamentalMatrixTransform,
  82. min_samples=8, residual_threshold=0.005,
  83. max_trials=200)
  84. # Ignore outliers
  85. ret = ret[inliers]
  86. Rt = extractPose(model.params)
  87. return idx1[inliers], idx2[inliers], Rt
  88. class Frame(object):
  89. def __init__(self, mapp, img, K):
  90. self.K = K
  91. self.Kinv = np.linalg.inv(self.K)
  92. self.pose = IRt
  93. self.id = len(mapp.frames)
  94. mapp.frames.append(self)
  95. pts, self.des = extract(img)
  96. if self.des.any()!=None:
  97. self.pts = normalize(self.Kinv, pts)