hough_transform.hpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Boost.GIL (Generic Image Library) - tests
  2. //
  3. // Copyright 2020 Olzhas Zhumabek <anonymous.from.applecity@gmail.com>
  4. //
  5. // Use, modification and distribution are subject to the Boost Software License,
  6. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  7. // http://www.boost.org/LICENSE_1_0.txt)
  8. //
  9. #ifndef BOOST_GIL_EXTENSION_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP
  10. #define BOOST_GIL_EXTENSION_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP
  11. #include <boost/gil/extension/image_processing/hough_parameter.hpp>
  12. #include <boost/gil/extension/rasterization/circle.hpp>
  13. #include <algorithm>
  14. #include <cmath>
  15. #include <cstddef>
  16. #include <iterator>
  17. #include <vector>
  18. namespace boost { namespace gil {
  19. /// \defgroup HoughTransform
  20. /// \brief A family of shape detectors that are specified by equation
  21. ///
  22. /// Hough transform is a method of mapping (voting) an object which can be described by
  23. /// equation to single point in accumulator array (also called parameter space).
  24. /// Each set pixel in edge map votes for every shape it can be part of.
  25. /// Circle and ellipse transforms are very costly to brute force, while
  26. /// non-brute-forcing algorithms tend to gamble on probabilities.
  27. /// \ingroup HoughTransform
  28. /// \brief Vote for best fit of a line in parameter space
  29. ///
  30. /// The input must be an edge map with grayscale pixels. Be aware of overflow inside
  31. /// accumulator array. The theta parameter is best computed through factory function
  32. /// provided in hough_parameter.hpp
  33. template <typename InputView, typename OutputView>
  34. void hough_line_transform(InputView const& input_view, OutputView const& accumulator_array,
  35. hough_parameter<double> const& theta,
  36. hough_parameter<std::ptrdiff_t> const& radius)
  37. {
  38. std::ptrdiff_t r_lower_bound = radius.start_point;
  39. std::ptrdiff_t r_upper_bound = r_lower_bound + radius.step_size * (radius.step_count - 1);
  40. for (std::ptrdiff_t y = 0; y < input_view.height(); ++y)
  41. {
  42. for (std::ptrdiff_t x = 0; x < input_view.width(); ++x)
  43. {
  44. if (!input_view(x, y)[0])
  45. {
  46. continue;
  47. }
  48. for (std::size_t theta_index = 0; theta_index < theta.step_count; ++theta_index)
  49. {
  50. double theta_current =
  51. theta.start_point + theta.step_size * static_cast<double>(theta_index);
  52. std::ptrdiff_t current_r =
  53. std::llround(static_cast<double>(x) * std::cos(theta_current) +
  54. static_cast<double>(y) * std::sin(theta_current));
  55. if (current_r < r_lower_bound || current_r > r_upper_bound)
  56. {
  57. continue;
  58. }
  59. std::size_t r_index = static_cast<std::size_t>(
  60. std::llround((current_r - radius.start_point) / radius.step_size));
  61. // one more safety guard to not get out of bounds
  62. if (r_index < radius.step_count)
  63. {
  64. accumulator_array(theta_index, r_index)[0] += 1;
  65. }
  66. }
  67. }
  68. }
  69. }
  70. /// \ingroup HoughTransform
  71. /// \brief Vote for best fit of a circle in parameter space according to rasterizer
  72. ///
  73. /// The input must be an edge map with grayscale pixels. Be aware of overflow inside
  74. /// accumulator array. Rasterizer is used to rasterize a circle for voting. The circle
  75. /// then is translated for every origin (x, y) in x y parameter space. For available
  76. /// circle rasterizers, please look at rasterization/circle.hpp
  77. template <typename ImageView, typename ForwardIterator, typename Rasterizer>
  78. void hough_circle_transform_brute(ImageView const& input,
  79. hough_parameter<std::ptrdiff_t> const& radius_parameter,
  80. hough_parameter<std::ptrdiff_t> const& x_parameter,
  81. hough_parameter<std::ptrdiff_t> const& y_parameter,
  82. ForwardIterator d_first, Rasterizer rasterizer)
  83. {
  84. for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; ++radius_index)
  85. {
  86. const auto radius = radius_parameter.start_point +
  87. radius_parameter.step_size * static_cast<std::ptrdiff_t>(radius_index);
  88. Rasterizer rasterizer{point_t{}, radius};
  89. std::vector<point_t> circle_points(rasterizer.point_count());
  90. rasterizer(circle_points.begin());
  91. // sort by scanline to improve cache coherence for row major images
  92. std::sort(circle_points.begin(), circle_points.end(),
  93. [](point_t const& lhs, point_t const& rhs) { return lhs.y < rhs.y; });
  94. const auto translate = [](std::vector<point_t>& points, point_t offset) {
  95. std::transform(points.begin(), points.end(), points.begin(), [offset](point_t point) {
  96. return point_t(point.x + offset.x, point.y + offset.y);
  97. });
  98. };
  99. // in case somebody passes iterator to likes of std::vector<bool>
  100. typename std::iterator_traits<ForwardIterator>::reference current_image = *d_first;
  101. // the algorithm has to traverse over parameter space and look at input, instead
  102. // of vice versa, as otherwise it will call translate too many times, as input
  103. // is usually bigger than the coordinate portion of parameter space.
  104. // This might cause extensive cache misses
  105. for (std::size_t x_index = 0; x_index < x_parameter.step_count; ++x_index)
  106. {
  107. for (std::size_t y_index = 0; y_index < y_parameter.step_count; ++y_index)
  108. {
  109. const std::ptrdiff_t x = x_parameter.start_point + x_index * x_parameter.step_size;
  110. const std::ptrdiff_t y = y_parameter.start_point + y_index * y_parameter.step_size;
  111. auto translated_circle = circle_points;
  112. translate(translated_circle, {x, y});
  113. for (const auto& point : translated_circle)
  114. {
  115. if (input(point))
  116. {
  117. ++current_image(x_index, y_index)[0];
  118. }
  119. }
  120. }
  121. }
  122. ++d_first;
  123. }
  124. }
  125. }} // namespace boost::gil
  126. #endif