match.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import numpy as np
  2. from scipy.spatial.distance import cdist
  3. def match_descriptors(
  4. descriptors1,
  5. descriptors2,
  6. metric=None,
  7. p=2,
  8. max_distance=np.inf,
  9. cross_check=True,
  10. max_ratio=1.0,
  11. ):
  12. """Brute-force matching of descriptors.
  13. For each descriptor in the first set this matcher finds the closest
  14. descriptor in the second set (and vice-versa in the case of enabled
  15. cross-checking).
  16. Parameters
  17. ----------
  18. descriptors1 : (M, P) array
  19. Descriptors of size P about M keypoints in the first image.
  20. descriptors2 : (N, P) array
  21. Descriptors of size P about N keypoints in the second image.
  22. metric : {'euclidean', 'cityblock', 'minkowski', 'hamming', ...} , optional
  23. The metric to compute the distance between two descriptors. See
  24. `scipy.spatial.distance.cdist` for all possible types. The hamming
  25. distance should be used for binary descriptors. By default the L2-norm
  26. is used for all descriptors of dtype float or double and the Hamming
  27. distance is used for binary descriptors automatically.
  28. p : int, optional
  29. The p-norm to apply for ``metric='minkowski'``.
  30. max_distance : float, optional
  31. Maximum allowed distance between descriptors of two keypoints
  32. in separate images to be regarded as a match.
  33. cross_check : bool, optional
  34. If True, the matched keypoints are returned after cross checking i.e. a
  35. matched pair (keypoint1, keypoint2) is returned if keypoint2 is the
  36. best match for keypoint1 in second image and keypoint1 is the best
  37. match for keypoint2 in first image.
  38. max_ratio : float, optional
  39. Maximum ratio of distances between first and second closest descriptor
  40. in the second set of descriptors. This threshold is useful to filter
  41. ambiguous matches between the two descriptor sets. The choice of this
  42. value depends on the statistics of the chosen descriptor, e.g.,
  43. for SIFT descriptors a value of 0.8 is usually chosen, see
  44. D.G. Lowe, "Distinctive Image Features from Scale-Invariant Keypoints",
  45. International Journal of Computer Vision, 2004.
  46. Returns
  47. -------
  48. matches : (Q, 2) array
  49. Indices of corresponding matches in first and second set of
  50. descriptors, where ``matches[:, 0]`` denote the indices in the first
  51. and ``matches[:, 1]`` the indices in the second set of descriptors.
  52. """
  53. if descriptors1.shape[1] != descriptors2.shape[1]:
  54. raise ValueError("Descriptor length must equal.")
  55. if metric is None:
  56. if np.issubdtype(descriptors1.dtype, bool):
  57. metric = 'hamming'
  58. else:
  59. metric = 'euclidean'
  60. kwargs = {}
  61. # Scipy raises an error if p is passed as an extra argument when it isn't
  62. # necessary for the chosen metric.
  63. if metric == 'minkowski':
  64. kwargs['p'] = p
  65. distances = cdist(descriptors1, descriptors2, metric=metric, **kwargs)
  66. indices1 = np.arange(descriptors1.shape[0])
  67. indices2 = np.argmin(distances, axis=1)
  68. if cross_check:
  69. matches1 = np.argmin(distances, axis=0)
  70. mask = indices1 == matches1[indices2]
  71. indices1 = indices1[mask]
  72. indices2 = indices2[mask]
  73. if max_distance < np.inf:
  74. mask = distances[indices1, indices2] < max_distance
  75. indices1 = indices1[mask]
  76. indices2 = indices2[mask]
  77. if max_ratio < 1.0:
  78. best_distances = distances[indices1, indices2]
  79. distances[indices1, indices2] = np.inf
  80. second_best_indices2 = np.argmin(distances[indices1], axis=1)
  81. second_best_distances = distances[indices1, second_best_indices2]
  82. second_best_distances[second_best_distances == 0] = np.finfo(np.float64).eps
  83. ratio = best_distances / second_best_distances
  84. mask = ratio < max_ratio
  85. indices1 = indices1[mask]
  86. indices2 = indices2[mask]
  87. matches = np.column_stack((indices1, indices2))
  88. return matches