numeric.hpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. //
  2. // Copyright 2019 Olzhas Zhumabek <anonymous.from.applecity@gmail.com>
  3. //
  4. // Use, modification and distribution are subject to the Boost Software License,
  5. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. #ifndef BOOST_GIL_IMAGE_PROCESSING_NUMERIC_HPP
  9. #define BOOST_GIL_IMAGE_PROCESSING_NUMERIC_HPP
  10. #include <boost/gil/extension/numeric/kernel.hpp>
  11. #include <boost/gil/extension/numeric/convolve.hpp>
  12. #include <boost/gil/image_view.hpp>
  13. #include <boost/gil/typedefs.hpp>
  14. #include <boost/gil/detail/math.hpp>
  15. // fixes ambigious call to std::abs, https://stackoverflow.com/a/30084734/4593721
  16. #include <cstdlib>
  17. #include <cmath>
  18. namespace boost { namespace gil {
  19. /// \defgroup ImageProcessingMath
  20. /// \brief Math operations for IP algorithms
  21. ///
  22. /// This is mostly handful of mathemtical operations that are required by other
  23. /// image processing algorithms
  24. ///
  25. /// \brief Normalized cardinal sine
  26. /// \ingroup ImageProcessingMath
  27. ///
  28. /// normalized_sinc(x) = sin(pi * x) / (pi * x)
  29. ///
  30. inline double normalized_sinc(double x)
  31. {
  32. return std::sin(x * boost::gil::pi) / (x * boost::gil::pi);
  33. }
  34. /// \brief Lanczos response at point x
  35. /// \ingroup ImageProcessingMath
  36. ///
  37. /// Lanczos response is defined as:
  38. /// x == 0: 1
  39. /// -a < x && x < a: 0
  40. /// otherwise: normalized_sinc(x) / normalized_sinc(x / a)
  41. inline double lanczos(double x, std::ptrdiff_t a)
  42. {
  43. // means == but <= avoids compiler warning
  44. if (0 <= x && x <= 0)
  45. return 1;
  46. if (-a < x && x < a)
  47. return normalized_sinc(x) / normalized_sinc(x / static_cast<double>(a));
  48. return 0;
  49. }
  50. inline void compute_tensor_entries(
  51. boost::gil::gray16s_view_t dx,
  52. boost::gil::gray16s_view_t dy,
  53. boost::gil::gray32f_view_t m11,
  54. boost::gil::gray32f_view_t m12_21,
  55. boost::gil::gray32f_view_t m22)
  56. {
  57. for (std::ptrdiff_t y = 0; y < dx.height(); ++y) {
  58. for (std::ptrdiff_t x = 0; x < dx.width(); ++x) {
  59. auto dx_value = dx(x, y);
  60. auto dy_value = dy(x, y);
  61. m11(x, y) = dx_value * dx_value;
  62. m12_21(x, y) = dx_value * dy_value;
  63. m22(x, y) = dy_value * dy_value;
  64. }
  65. }
  66. }
  67. /// \brief Generate mean kernel
  68. /// \ingroup ImageProcessingMath
  69. ///
  70. /// Fills supplied view with normalized mean
  71. /// in which all entries will be equal to
  72. /// \code 1 / (dst.size()) \endcode
  73. template <typename T = float, typename Allocator = std::allocator<T>>
  74. inline detail::kernel_2d<T, Allocator> generate_normalized_mean(std::size_t side_length)
  75. {
  76. if (side_length % 2 != 1)
  77. throw std::invalid_argument("kernel dimensions should be odd and equal");
  78. const float entry = 1.0f / static_cast<float>(side_length * side_length);
  79. detail::kernel_2d<T, Allocator> result(side_length, side_length / 2, side_length / 2);
  80. for (auto& cell: result) {
  81. cell = entry;
  82. }
  83. return result;
  84. }
  85. /// \brief Generate kernel with all 1s
  86. /// \ingroup ImageProcessingMath
  87. ///
  88. /// Fills supplied view with 1s (ones)
  89. template <typename T = float, typename Allocator = std::allocator<T>>
  90. inline detail::kernel_2d<T, Allocator> generate_unnormalized_mean(std::size_t side_length)
  91. {
  92. if (side_length % 2 != 1)
  93. throw std::invalid_argument("kernel dimensions should be odd and equal");
  94. detail::kernel_2d<T, Allocator> result(side_length, side_length / 2, side_length / 2);
  95. for (auto& cell: result) {
  96. cell = 1.0f;
  97. }
  98. return result;
  99. }
  100. /// \brief Generate Gaussian kernel
  101. /// \ingroup ImageProcessingMath
  102. ///
  103. /// Fills supplied view with values taken from Gaussian distribution. See
  104. /// https://en.wikipedia.org/wiki/Gaussian_blur
  105. template <typename T = float, typename Allocator = std::allocator<T>>
  106. inline detail::kernel_2d<T, Allocator> generate_gaussian_kernel(std::size_t side_length, double sigma)
  107. {
  108. if (side_length % 2 != 1)
  109. throw std::invalid_argument("kernel dimensions should be odd and equal");
  110. const double denominator = 2 * boost::gil::pi * sigma * sigma;
  111. auto middle = side_length / 2;
  112. std::vector<T, Allocator> values(side_length * side_length);
  113. for (std::size_t y = 0; y < side_length; ++y)
  114. {
  115. for (std::size_t x = 0; x < side_length; ++x)
  116. {
  117. const auto delta_x = middle > x ? middle - x : x - middle;
  118. const auto delta_y = middle > y ? middle - y : y - middle;
  119. const double power = (delta_x * delta_x + delta_y * delta_y) / (2 * sigma * sigma);
  120. const double nominator = std::exp(-power);
  121. const float value = static_cast<float>(nominator / denominator);
  122. values[y * side_length + x] = value;
  123. }
  124. }
  125. return detail::kernel_2d<T, Allocator>(values.begin(), values.size(), middle, middle);
  126. }
  127. /// \brief Generates Sobel operator in horizontal direction
  128. /// \ingroup ImageProcessingMath
  129. ///
  130. /// Generates a kernel which will represent Sobel operator in
  131. /// horizontal direction of specified degree (no need to convolve multiple times
  132. /// to obtain the desired degree).
  133. /// https://www.researchgate.net/publication/239398674_An_Isotropic_3_3_Image_Gradient_Operator
  134. template <typename T = float, typename Allocator = std::allocator<T>>
  135. inline detail::kernel_2d<T, Allocator> generate_dx_sobel(unsigned int degree = 1)
  136. {
  137. switch (degree)
  138. {
  139. case 0:
  140. {
  141. return get_identity_kernel<T, Allocator>();
  142. }
  143. case 1:
  144. {
  145. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  146. std::copy(dx_sobel.begin(), dx_sobel.end(), result.begin());
  147. return result;
  148. }
  149. default:
  150. throw std::logic_error("not supported yet");
  151. }
  152. //to not upset compiler
  153. throw std::runtime_error("unreachable statement");
  154. }
  155. /// \brief Generate Scharr operator in horizontal direction
  156. /// \ingroup ImageProcessingMath
  157. ///
  158. /// Generates a kernel which will represent Scharr operator in
  159. /// horizontal direction of specified degree (no need to convolve multiple times
  160. /// to obtain the desired degree).
  161. /// https://www.researchgate.net/profile/Hanno_Scharr/publication/220955743_Optimal_Filters_for_Extended_Optical_Flow/links/004635151972eda98f000000/Optimal-Filters-for-Extended-Optical-Flow.pdf
  162. template <typename T = float, typename Allocator = std::allocator<T>>
  163. inline detail::kernel_2d<T, Allocator> generate_dx_scharr(unsigned int degree = 1)
  164. {
  165. switch (degree)
  166. {
  167. case 0:
  168. {
  169. return get_identity_kernel<T, Allocator>();
  170. }
  171. case 1:
  172. {
  173. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  174. std::copy(dx_scharr.begin(), dx_scharr.end(), result.begin());
  175. return result;
  176. }
  177. default:
  178. throw std::logic_error("not supported yet");
  179. }
  180. //to not upset compiler
  181. throw std::runtime_error("unreachable statement");
  182. }
  183. /// \brief Generates Sobel operator in vertical direction
  184. /// \ingroup ImageProcessingMath
  185. ///
  186. /// Generates a kernel which will represent Sobel operator in
  187. /// vertical direction of specified degree (no need to convolve multiple times
  188. /// to obtain the desired degree).
  189. /// https://www.researchgate.net/publication/239398674_An_Isotropic_3_3_Image_Gradient_Operator
  190. template <typename T = float, typename Allocator = std::allocator<T>>
  191. inline detail::kernel_2d<T, Allocator> generate_dy_sobel(unsigned int degree = 1)
  192. {
  193. switch (degree)
  194. {
  195. case 0:
  196. {
  197. return get_identity_kernel<T, Allocator>();
  198. }
  199. case 1:
  200. {
  201. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  202. std::copy(dy_sobel.begin(), dy_sobel.end(), result.begin());
  203. return result;
  204. }
  205. default:
  206. throw std::logic_error("not supported yet");
  207. }
  208. //to not upset compiler
  209. throw std::runtime_error("unreachable statement");
  210. }
  211. /// \brief Generate Scharr operator in vertical direction
  212. /// \ingroup ImageProcessingMath
  213. ///
  214. /// Generates a kernel which will represent Scharr operator in
  215. /// vertical direction of specified degree (no need to convolve multiple times
  216. /// to obtain the desired degree).
  217. /// https://www.researchgate.net/profile/Hanno_Scharr/publication/220955743_Optimal_Filters_for_Extended_Optical_Flow/links/004635151972eda98f000000/Optimal-Filters-for-Extended-Optical-Flow.pdf
  218. template <typename T = float, typename Allocator = std::allocator<T>>
  219. inline detail::kernel_2d<T, Allocator> generate_dy_scharr(unsigned int degree = 1)
  220. {
  221. switch (degree)
  222. {
  223. case 0:
  224. {
  225. return get_identity_kernel<T, Allocator>();
  226. }
  227. case 1:
  228. {
  229. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  230. std::copy(dy_scharr.begin(), dy_scharr.end(), result.begin());
  231. return result;
  232. }
  233. default:
  234. throw std::logic_error("not supported yet");
  235. }
  236. //to not upset compiler
  237. throw std::runtime_error("unreachable statement");
  238. }
  239. /// \brief Compute xy gradient, and second order x and y gradients
  240. /// \ingroup ImageProcessingMath
  241. ///
  242. /// Hessian matrix is defined as a matrix of partial derivates
  243. /// for 2d case, it is [[ddxx, dxdy], [dxdy, ddyy].
  244. /// d stands for derivative, and x or y stand for direction.
  245. /// For example, dx stands for derivative (gradient) in horizontal
  246. /// direction, and ddxx means second order derivative in horizon direction
  247. /// https://en.wikipedia.org/wiki/Hessian_matrix
  248. template <typename GradientView, typename OutputView>
  249. inline void compute_hessian_entries(
  250. GradientView dx,
  251. GradientView dy,
  252. OutputView ddxx,
  253. OutputView dxdy,
  254. OutputView ddyy)
  255. {
  256. auto sobel_x = generate_dx_sobel();
  257. auto sobel_y = generate_dy_sobel();
  258. detail::convolve_2d(dx, sobel_x, ddxx);
  259. detail::convolve_2d(dx, sobel_y, dxdy);
  260. detail::convolve_2d(dy, sobel_y, ddyy);
  261. }
  262. }} // namespace boost::gil
  263. #endif