numeric.hpp 11 KB

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