test_hausdorff.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. from __future__ import division, absolute_import, print_function
  2. import numpy as np
  3. from numpy.testing import (assert_almost_equal,
  4. assert_array_equal,
  5. assert_equal,
  6. assert_)
  7. from scipy.spatial.distance import directed_hausdorff
  8. from scipy.spatial import distance
  9. from scipy._lib._util import check_random_state
  10. class TestHausdorff(object):
  11. # Test various properties of the directed Hausdorff code.
  12. def setup_method(self):
  13. np.random.seed(1234)
  14. random_angles = np.random.random(100) * np.pi * 2
  15. random_columns = np.column_stack(
  16. (random_angles, random_angles, np.zeros(100)))
  17. random_columns[..., 0] = np.cos(random_columns[..., 0])
  18. random_columns[..., 1] = np.sin(random_columns[..., 1])
  19. random_columns_2 = np.column_stack(
  20. (random_angles, random_angles, np.zeros(100)))
  21. random_columns_2[1:, 0] = np.cos(random_columns_2[1:, 0]) * 2.0
  22. random_columns_2[1:, 1] = np.sin(random_columns_2[1:, 1]) * 2.0
  23. # move one point farther out so we don't have two perfect circles
  24. random_columns_2[0, 0] = np.cos(random_columns_2[0, 0]) * 3.3
  25. random_columns_2[0, 1] = np.sin(random_columns_2[0, 1]) * 3.3
  26. self.path_1 = random_columns
  27. self.path_2 = random_columns_2
  28. self.path_1_4d = np.insert(self.path_1, 3, 5, axis=1)
  29. self.path_2_4d = np.insert(self.path_2, 3, 27, axis=1)
  30. def test_symmetry(self):
  31. # Ensure that the directed (asymmetric) Hausdorff distance is
  32. # actually asymmetric
  33. forward = directed_hausdorff(self.path_1, self.path_2)[0]
  34. reverse = directed_hausdorff(self.path_2, self.path_1)[0]
  35. assert_(forward != reverse)
  36. def test_brute_force_comparison_forward(self):
  37. # Ensure that the algorithm for directed_hausdorff gives the
  38. # same result as the simple / brute force approach in the
  39. # forward direction.
  40. actual = directed_hausdorff(self.path_1, self.path_2)[0]
  41. # brute force over rows:
  42. expected = max(np.amin(distance.cdist(self.path_1, self.path_2),
  43. axis=1))
  44. assert_almost_equal(actual, expected, decimal=9)
  45. def test_brute_force_comparison_reverse(self):
  46. # Ensure that the algorithm for directed_hausdorff gives the
  47. # same result as the simple / brute force approach in the
  48. # reverse direction.
  49. actual = directed_hausdorff(self.path_2, self.path_1)[0]
  50. # brute force over columns:
  51. expected = max(np.amin(distance.cdist(self.path_1, self.path_2),
  52. axis=0))
  53. assert_almost_equal(actual, expected, decimal=9)
  54. def test_degenerate_case(self):
  55. # The directed Hausdorff distance must be zero if both input
  56. # data arrays match.
  57. actual = directed_hausdorff(self.path_1, self.path_1)[0]
  58. assert_almost_equal(actual, 0.0, decimal=9)
  59. def test_2d_data_forward(self):
  60. # Ensure that 2D data is handled properly for a simple case
  61. # relative to brute force approach.
  62. actual = directed_hausdorff(self.path_1[..., :2],
  63. self.path_2[..., :2])[0]
  64. expected = max(np.amin(distance.cdist(self.path_1[..., :2],
  65. self.path_2[..., :2]),
  66. axis=1))
  67. assert_almost_equal(actual, expected, decimal=9)
  68. def test_4d_data_reverse(self):
  69. # Ensure that 4D data is handled properly for a simple case
  70. # relative to brute force approach.
  71. actual = directed_hausdorff(self.path_2_4d, self.path_1_4d)[0]
  72. # brute force over columns:
  73. expected = max(np.amin(distance.cdist(self.path_1_4d, self.path_2_4d),
  74. axis=0))
  75. assert_almost_equal(actual, expected, decimal=9)
  76. def test_indices(self):
  77. # Ensure that correct point indices are returned -- they should
  78. # correspond to the Hausdorff pair
  79. path_simple_1 = np.array([[-1,-12],[0,0], [1,1], [3,7], [1,2]])
  80. path_simple_2 = np.array([[0,0], [1,1], [4,100], [10,9]])
  81. actual = directed_hausdorff(path_simple_2, path_simple_1)[1:]
  82. expected = (2, 3)
  83. assert_array_equal(actual, expected)
  84. def test_random_state(self):
  85. # ensure that the global random state is not modified because
  86. # the directed Hausdorff algorithm uses randomization
  87. rs = check_random_state(None)
  88. old_global_state = rs.get_state()
  89. directed_hausdorff(self.path_1, self.path_2)
  90. rs2 = check_random_state(None)
  91. new_global_state = rs2.get_state()
  92. assert_equal(new_global_state, old_global_state)
  93. def test_random_state_None_int(self):
  94. # check that seed values of None or int do not alter global
  95. # random state
  96. for seed in [None, 27870671]:
  97. rs = check_random_state(None)
  98. old_global_state = rs.get_state()
  99. directed_hausdorff(self.path_1, self.path_2, seed)
  100. rs2 = check_random_state(None)
  101. new_global_state = rs2.get_state()
  102. assert_equal(new_global_state, old_global_state)