Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:51:45

0001 // Boost.GIL (Generic Image Library) - tests
0002 //
0003 // Copyright 2020 Olzhas Zhumabek <anonymous.from.applecity@gmail.com>
0004 //
0005 // Use, modification and distribution are subject to the Boost Software License,
0006 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0007 // http://www.boost.org/LICENSE_1_0.txt)
0008 //
0009 #ifndef BOOST_GIL_EXTENSION_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP
0010 #define BOOST_GIL_EXTENSION_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP
0011 
0012 #include <boost/gil/extension/image_processing/hough_parameter.hpp>
0013 #include <boost/gil/extension/rasterization/circle.hpp>
0014 
0015 #include <algorithm>
0016 #include <cmath>
0017 #include <cstddef>
0018 #include <iterator>
0019 #include <vector>
0020 
0021 namespace boost { namespace gil {
0022 /// \defgroup HoughTransform
0023 /// \brief A family of shape detectors that are specified by equation
0024 ///
0025 /// Hough transform is a method of mapping (voting) an object which can be described by
0026 /// equation to single point in accumulator array (also called parameter space).
0027 /// Each set pixel in edge map votes for every shape it can be part of.
0028 /// Circle and ellipse transforms are very costly to brute force, while
0029 /// non-brute-forcing algorithms tend to gamble on probabilities.
0030 
0031 /// \ingroup HoughTransform
0032 /// \brief Vote for best fit of a line in parameter space
0033 ///
0034 /// The input must be an edge map with grayscale pixels. Be aware of overflow inside
0035 /// accumulator array. The theta parameter is best computed through factory function
0036 /// provided in hough_parameter.hpp
0037 template <typename InputView, typename OutputView>
0038 void hough_line_transform(InputView const& input_view, OutputView const& accumulator_array,
0039                           hough_parameter<double> const& theta,
0040                           hough_parameter<std::ptrdiff_t> const& radius)
0041 {
0042     std::ptrdiff_t r_lower_bound = radius.start_point;
0043     std::ptrdiff_t r_upper_bound = r_lower_bound + radius.step_size * (radius.step_count - 1);
0044 
0045     for (std::ptrdiff_t y = 0; y < input_view.height(); ++y)
0046     {
0047         for (std::ptrdiff_t x = 0; x < input_view.width(); ++x)
0048         {
0049             if (!input_view(x, y)[0])
0050             {
0051                 continue;
0052             }
0053 
0054             for (std::size_t theta_index = 0; theta_index < theta.step_count; ++theta_index)
0055             {
0056                 double theta_current =
0057                     theta.start_point + theta.step_size * static_cast<double>(theta_index);
0058                 std::ptrdiff_t current_r =
0059                     std::llround(static_cast<double>(x) * std::cos(theta_current) +
0060                                  static_cast<double>(y) * std::sin(theta_current));
0061                 if (current_r < r_lower_bound || current_r > r_upper_bound)
0062                 {
0063                     continue;
0064                 }
0065                 std::size_t r_index = static_cast<std::size_t>(
0066                     std::llround((current_r - radius.start_point) / radius.step_size));
0067                 // one more safety guard to not get out of bounds
0068                 if (r_index < radius.step_count)
0069                 {
0070                     accumulator_array(theta_index, r_index)[0] += 1;
0071                 }
0072             }
0073         }
0074     }
0075 }
0076 
0077 /// \ingroup HoughTransform
0078 /// \brief Vote for best fit of a circle in parameter space according to rasterizer
0079 ///
0080 /// The input must be an edge map with grayscale pixels. Be aware of overflow inside
0081 /// accumulator array. Rasterizer is used to rasterize a circle for voting. The circle
0082 /// then is translated for every origin (x, y) in x y parameter space. For available
0083 /// circle rasterizers, please look at rasterization/circle.hpp
0084 template <typename ImageView, typename ForwardIterator, typename Rasterizer>
0085 void hough_circle_transform_brute(ImageView const& input,
0086                                   hough_parameter<std::ptrdiff_t> const& radius_parameter,
0087                                   hough_parameter<std::ptrdiff_t> const& x_parameter,
0088                                   hough_parameter<std::ptrdiff_t> const& y_parameter,
0089                                   ForwardIterator d_first, Rasterizer rasterizer)
0090 {
0091     for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; ++radius_index)
0092     {
0093         const auto radius = radius_parameter.start_point +
0094                             radius_parameter.step_size * static_cast<std::ptrdiff_t>(radius_index);
0095         Rasterizer rasterizer{point_t{}, radius};
0096         std::vector<point_t> circle_points(rasterizer.point_count());
0097         rasterizer(circle_points.begin());
0098         // sort by scanline to improve cache coherence for row major images
0099         std::sort(circle_points.begin(), circle_points.end(),
0100                   [](point_t const& lhs, point_t const& rhs) { return lhs.y < rhs.y; });
0101         const auto translate = [](std::vector<point_t>& points, point_t offset) {
0102             std::transform(points.begin(), points.end(), points.begin(), [offset](point_t point) {
0103                 return point_t(point.x + offset.x, point.y + offset.y);
0104             });
0105         };
0106 
0107         // in case somebody passes iterator to likes of std::vector<bool>
0108         typename std::iterator_traits<ForwardIterator>::reference current_image = *d_first;
0109 
0110         // the algorithm has to traverse over parameter space and look at input, instead
0111         // of vice versa, as otherwise it will call translate too many times, as input
0112         // is usually bigger than the coordinate portion of parameter space.
0113         // This might cause extensive cache misses
0114         for (std::size_t x_index = 0; x_index < x_parameter.step_count; ++x_index)
0115         {
0116             for (std::size_t y_index = 0; y_index < y_parameter.step_count; ++y_index)
0117             {
0118                 const std::ptrdiff_t x = x_parameter.start_point + x_index * x_parameter.step_size;
0119                 const std::ptrdiff_t y = y_parameter.start_point + y_index * y_parameter.step_size;
0120 
0121                 auto translated_circle = circle_points;
0122                 translate(translated_circle, {x, y});
0123                 for (const auto& point : translated_circle)
0124                 {
0125                     if (input(point))
0126                     {
0127                         ++current_image(x_index, y_index)[0];
0128                     }
0129                 }
0130             }
0131         }
0132         ++d_first;
0133     }
0134 }
0135 
0136 }} // namespace boost::gil
0137 
0138 #endif