numeric.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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::detail::pi) / (x * boost::gil::detail::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. #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
  51. #pragma warning(push)
  52. #pragma warning(disable:4244) // 'argument': conversion from 'const Channel' to 'BaseChannelValue', possible loss of data
  53. #endif
  54. inline void compute_tensor_entries(
  55. boost::gil::gray16s_view_t dx,
  56. boost::gil::gray16s_view_t dy,
  57. boost::gil::gray32f_view_t m11,
  58. boost::gil::gray32f_view_t m12_21,
  59. boost::gil::gray32f_view_t m22)
  60. {
  61. for (std::ptrdiff_t y = 0; y < dx.height(); ++y) {
  62. for (std::ptrdiff_t x = 0; x < dx.width(); ++x) {
  63. auto dx_value = dx(x, y);
  64. auto dy_value = dy(x, y);
  65. m11(x, y) = dx_value * dx_value;
  66. m12_21(x, y) = dx_value * dy_value;
  67. m22(x, y) = dy_value * dy_value;
  68. }
  69. }
  70. }
  71. #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
  72. #pragma warning(pop)
  73. #endif
  74. /// \brief Generate mean kernel
  75. /// \ingroup ImageProcessingMath
  76. ///
  77. /// Fills supplied view with normalized mean
  78. /// in which all entries will be equal to
  79. /// \code 1 / (dst.size()) \endcode
  80. template <typename T = float, typename Allocator = std::allocator<T>>
  81. inline detail::kernel_2d<T, Allocator> generate_normalized_mean(std::size_t side_length)
  82. {
  83. if (side_length % 2 != 1)
  84. throw std::invalid_argument("kernel dimensions should be odd and equal");
  85. const float entry = 1.0f / static_cast<float>(side_length * side_length);
  86. detail::kernel_2d<T, Allocator> result(side_length, side_length / 2, side_length / 2);
  87. for (auto& cell: result) {
  88. cell = entry;
  89. }
  90. return result;
  91. }
  92. /// \brief Generate kernel with all 1s
  93. /// \ingroup ImageProcessingMath
  94. ///
  95. /// Fills supplied view with 1s (ones)
  96. template <typename T = float, typename Allocator = std::allocator<T>>
  97. inline detail::kernel_2d<T, Allocator> generate_unnormalized_mean(std::size_t side_length)
  98. {
  99. if (side_length % 2 != 1)
  100. throw std::invalid_argument("kernel dimensions should be odd and equal");
  101. detail::kernel_2d<T, Allocator> result(side_length, side_length / 2, side_length / 2);
  102. for (auto& cell: result) {
  103. cell = 1.0f;
  104. }
  105. return result;
  106. }
  107. /// \brief Generate Gaussian kernel
  108. /// \ingroup ImageProcessingMath
  109. ///
  110. /// Fills supplied view with values taken from Gaussian distribution. See
  111. /// https://en.wikipedia.org/wiki/Gaussian_blur
  112. template <typename T = float, typename Allocator = std::allocator<T>>
  113. inline detail::kernel_2d<T, Allocator> generate_gaussian_kernel(std::size_t side_length, double sigma)
  114. {
  115. if (side_length % 2 != 1)
  116. throw std::invalid_argument("kernel dimensions should be odd and equal");
  117. const double denominator = 2 * boost::gil::detail::pi * sigma * sigma;
  118. auto middle = side_length / 2;
  119. std::vector<T, Allocator> values(side_length * side_length);
  120. for (std::size_t y = 0; y < side_length; ++y)
  121. {
  122. for (std::size_t x = 0; x < side_length; ++x)
  123. {
  124. const auto delta_x = middle > x ? middle - x : x - middle;
  125. const auto delta_y = middle > y ? middle - y : y - middle;
  126. const double power = (delta_x * delta_x + delta_y * delta_y) / (2 * sigma * sigma);
  127. const double nominator = std::exp(-power);
  128. const float value = static_cast<float>(nominator / denominator);
  129. values[y * side_length + x] = value;
  130. }
  131. }
  132. return detail::kernel_2d<T, Allocator>(values.begin(), values.size(), middle, middle);
  133. }
  134. /// \brief Generates Sobel operator in horizontal direction
  135. /// \ingroup ImageProcessingMath
  136. ///
  137. /// Generates a kernel which will represent Sobel operator in
  138. /// horizontal direction of specified degree (no need to convolve multiple times
  139. /// to obtain the desired degree).
  140. /// https://www.researchgate.net/publication/239398674_An_Isotropic_3_3_Image_Gradient_Operator
  141. template <typename T = float, typename Allocator = std::allocator<T>>
  142. inline detail::kernel_2d<T, Allocator> generate_dx_sobel(unsigned int degree = 1)
  143. {
  144. switch (degree)
  145. {
  146. case 0:
  147. {
  148. return detail::get_identity_kernel<T, Allocator>();
  149. }
  150. case 1:
  151. {
  152. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  153. std::copy(detail::dx_sobel.begin(), detail::dx_sobel.end(), result.begin());
  154. return result;
  155. }
  156. default:
  157. throw std::logic_error("not supported yet");
  158. }
  159. //to not upset compiler
  160. throw std::runtime_error("unreachable statement");
  161. }
  162. /// \brief Generate Scharr operator in horizontal direction
  163. /// \ingroup ImageProcessingMath
  164. ///
  165. /// Generates a kernel which will represent Scharr operator in
  166. /// horizontal direction of specified degree (no need to convolve multiple times
  167. /// to obtain the desired degree).
  168. /// https://www.researchgate.net/profile/Hanno_Scharr/publication/220955743_Optimal_Filters_for_Extended_Optical_Flow/links/004635151972eda98f000000/Optimal-Filters-for-Extended-Optical-Flow.pdf
  169. template <typename T = float, typename Allocator = std::allocator<T>>
  170. inline detail::kernel_2d<T, Allocator> generate_dx_scharr(unsigned int degree = 1)
  171. {
  172. switch (degree)
  173. {
  174. case 0:
  175. {
  176. return detail::get_identity_kernel<T, Allocator>();
  177. }
  178. case 1:
  179. {
  180. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  181. std::copy(detail::dx_scharr.begin(), detail::dx_scharr.end(), result.begin());
  182. return result;
  183. }
  184. default:
  185. throw std::logic_error("not supported yet");
  186. }
  187. //to not upset compiler
  188. throw std::runtime_error("unreachable statement");
  189. }
  190. /// \brief Generates Sobel operator in vertical direction
  191. /// \ingroup ImageProcessingMath
  192. ///
  193. /// Generates a kernel which will represent Sobel operator in
  194. /// vertical direction of specified degree (no need to convolve multiple times
  195. /// to obtain the desired degree).
  196. /// https://www.researchgate.net/publication/239398674_An_Isotropic_3_3_Image_Gradient_Operator
  197. template <typename T = float, typename Allocator = std::allocator<T>>
  198. inline detail::kernel_2d<T, Allocator> generate_dy_sobel(unsigned int degree = 1)
  199. {
  200. switch (degree)
  201. {
  202. case 0:
  203. {
  204. return detail::get_identity_kernel<T, Allocator>();
  205. }
  206. case 1:
  207. {
  208. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  209. std::copy(detail::dy_sobel.begin(), detail::dy_sobel.end(), result.begin());
  210. return result;
  211. }
  212. default:
  213. throw std::logic_error("not supported yet");
  214. }
  215. //to not upset compiler
  216. throw std::runtime_error("unreachable statement");
  217. }
  218. /// \brief Generate Scharr operator in vertical direction
  219. /// \ingroup ImageProcessingMath
  220. ///
  221. /// Generates a kernel which will represent Scharr operator in
  222. /// vertical direction of specified degree (no need to convolve multiple times
  223. /// to obtain the desired degree).
  224. /// https://www.researchgate.net/profile/Hanno_Scharr/publication/220955743_Optimal_Filters_for_Extended_Optical_Flow/links/004635151972eda98f000000/Optimal-Filters-for-Extended-Optical-Flow.pdf
  225. template <typename T = float, typename Allocator = std::allocator<T>>
  226. inline detail::kernel_2d<T, Allocator> generate_dy_scharr(unsigned int degree = 1)
  227. {
  228. switch (degree)
  229. {
  230. case 0:
  231. {
  232. return detail::get_identity_kernel<T, Allocator>();
  233. }
  234. case 1:
  235. {
  236. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  237. std::copy(detail::dy_scharr.begin(), detail::dy_scharr.end(), result.begin());
  238. return result;
  239. }
  240. default:
  241. throw std::logic_error("not supported yet");
  242. }
  243. //to not upset compiler
  244. throw std::runtime_error("unreachable statement");
  245. }
  246. /// \brief Compute xy gradient, and second order x and y gradients
  247. /// \ingroup ImageProcessingMath
  248. ///
  249. /// Hessian matrix is defined as a matrix of partial derivates
  250. /// for 2d case, it is [[ddxx, dxdy], [dxdy, ddyy].
  251. /// d stands for derivative, and x or y stand for direction.
  252. /// For example, dx stands for derivative (gradient) in horizontal
  253. /// direction, and ddxx means second order derivative in horizon direction
  254. /// https://en.wikipedia.org/wiki/Hessian_matrix
  255. template <typename GradientView, typename OutputView>
  256. inline void compute_hessian_entries(
  257. GradientView dx,
  258. GradientView dy,
  259. OutputView ddxx,
  260. OutputView dxdy,
  261. OutputView ddyy)
  262. {
  263. auto sobel_x = generate_dx_sobel();
  264. auto sobel_y = generate_dy_sobel();
  265. detail::convolve_2d(dx, sobel_x, ddxx);
  266. detail::convolve_2d(dx, sobel_y, dxdy);
  267. detail::convolve_2d(dy, sobel_y, ddyy);
  268. }
  269. }} // namespace boost::gil
  270. #endif