numeric.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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 const middle = side_length / 2;
  123. std::vector<T, Allocator> values(side_length * side_length);
  124. T sum{0};
  125. for (std::size_t y = 0; y < side_length; ++y)
  126. {
  127. for (std::size_t x = 0; x < side_length; ++x)
  128. {
  129. const auto delta_x = x - middle;
  130. const auto delta_y = y - middle;
  131. const auto power = static_cast<double>(delta_x * delta_x + delta_y * delta_y) / (2 * sigma * sigma);
  132. const double nominator = std::exp(-power);
  133. const auto value = static_cast<T>(nominator / denominator);
  134. values[y * side_length + x] = value;
  135. sum += value;
  136. }
  137. }
  138. // normalize so that Gaussian kernel sums up to 1.
  139. std::transform(values.begin(), values.end(), values.begin(), [&sum](const auto & v) { return v/sum; });
  140. return detail::kernel_2d<T, Allocator>(values.begin(), values.size(), middle, middle);
  141. }
  142. /// \brief Generates Sobel operator in horizontal direction
  143. /// \ingroup ImageProcessingMath
  144. ///
  145. /// Generates a kernel which will represent Sobel operator in
  146. /// horizontal direction of specified degree (no need to convolve multiple times
  147. /// to obtain the desired degree).
  148. /// https://www.researchgate.net/publication/239398674_An_Isotropic_3_3_Image_Gradient_Operator
  149. template <typename T = float, typename Allocator = std::allocator<T>>
  150. inline auto generate_dx_sobel(unsigned int degree = 1)
  151. -> detail::kernel_2d<T, Allocator>
  152. {
  153. switch (degree)
  154. {
  155. case 0:
  156. {
  157. return detail::get_identity_kernel<T, Allocator>();
  158. }
  159. case 1:
  160. {
  161. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  162. std::copy(detail::dx_sobel.begin(), detail::dx_sobel.end(), result.begin());
  163. return result;
  164. }
  165. default:
  166. throw std::logic_error("not supported yet");
  167. }
  168. //to not upset compiler
  169. throw std::runtime_error("unreachable statement");
  170. }
  171. /// \brief Generate Scharr operator in horizontal direction
  172. /// \ingroup ImageProcessingMath
  173. ///
  174. /// Generates a kernel which will represent Scharr operator in
  175. /// horizontal direction of specified degree (no need to convolve multiple times
  176. /// to obtain the desired degree).
  177. /// https://www.researchgate.net/profile/Hanno_Scharr/publication/220955743_Optimal_Filters_for_Extended_Optical_Flow/links/004635151972eda98f000000/Optimal-Filters-for-Extended-Optical-Flow.pdf
  178. template <typename T = float, typename Allocator = std::allocator<T>>
  179. inline auto generate_dx_scharr(unsigned int degree = 1)
  180. -> detail::kernel_2d<T, Allocator>
  181. {
  182. switch (degree)
  183. {
  184. case 0:
  185. {
  186. return detail::get_identity_kernel<T, Allocator>();
  187. }
  188. case 1:
  189. {
  190. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  191. std::copy(detail::dx_scharr.begin(), detail::dx_scharr.end(), result.begin());
  192. return result;
  193. }
  194. default:
  195. throw std::logic_error("not supported yet");
  196. }
  197. //to not upset compiler
  198. throw std::runtime_error("unreachable statement");
  199. }
  200. /// \brief Generates Sobel operator in vertical direction
  201. /// \ingroup ImageProcessingMath
  202. ///
  203. /// Generates a kernel which will represent Sobel operator in
  204. /// vertical direction of specified degree (no need to convolve multiple times
  205. /// to obtain the desired degree).
  206. /// https://www.researchgate.net/publication/239398674_An_Isotropic_3_3_Image_Gradient_Operator
  207. template <typename T = float, typename Allocator = std::allocator<T>>
  208. inline auto generate_dy_sobel(unsigned int degree = 1)
  209. -> detail::kernel_2d<T, Allocator>
  210. {
  211. switch (degree)
  212. {
  213. case 0:
  214. {
  215. return detail::get_identity_kernel<T, Allocator>();
  216. }
  217. case 1:
  218. {
  219. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  220. std::copy(detail::dy_sobel.begin(), detail::dy_sobel.end(), result.begin());
  221. return result;
  222. }
  223. default:
  224. throw std::logic_error("not supported yet");
  225. }
  226. //to not upset compiler
  227. throw std::runtime_error("unreachable statement");
  228. }
  229. /// \brief Generate Scharr operator in vertical direction
  230. /// \ingroup ImageProcessingMath
  231. ///
  232. /// Generates a kernel which will represent Scharr operator in
  233. /// vertical direction of specified degree (no need to convolve multiple times
  234. /// to obtain the desired degree).
  235. /// https://www.researchgate.net/profile/Hanno_Scharr/publication/220955743_Optimal_Filters_for_Extended_Optical_Flow/links/004635151972eda98f000000/Optimal-Filters-for-Extended-Optical-Flow.pdf
  236. template <typename T = float, typename Allocator = std::allocator<T>>
  237. inline auto generate_dy_scharr(unsigned int degree = 1)
  238. -> detail::kernel_2d<T, Allocator>
  239. {
  240. switch (degree)
  241. {
  242. case 0:
  243. {
  244. return detail::get_identity_kernel<T, Allocator>();
  245. }
  246. case 1:
  247. {
  248. detail::kernel_2d<T, Allocator> result(3, 1, 1);
  249. std::copy(detail::dy_scharr.begin(), detail::dy_scharr.end(), result.begin());
  250. return result;
  251. }
  252. default:
  253. throw std::logic_error("not supported yet");
  254. }
  255. //to not upset compiler
  256. throw std::runtime_error("unreachable statement");
  257. }
  258. /// \brief Compute xy gradient, and second order x and y gradients
  259. /// \ingroup ImageProcessingMath
  260. ///
  261. /// Hessian matrix is defined as a matrix of partial derivates
  262. /// for 2d case, it is [[ddxx, dxdy], [dxdy, ddyy].
  263. /// d stands for derivative, and x or y stand for direction.
  264. /// For example, dx stands for derivative (gradient) in horizontal
  265. /// direction, and ddxx means second order derivative in horizon direction
  266. /// https://en.wikipedia.org/wiki/Hessian_matrix
  267. template <typename GradientView, typename OutputView>
  268. inline void compute_hessian_entries(
  269. GradientView dx,
  270. GradientView dy,
  271. OutputView ddxx,
  272. OutputView dxdy,
  273. OutputView ddyy)
  274. {
  275. auto sobel_x = generate_dx_sobel();
  276. auto sobel_y = generate_dy_sobel();
  277. detail::convolve_2d(dx, sobel_x, ddxx);
  278. detail::convolve_2d(dx, sobel_y, dxdy);
  279. detail::convolve_2d(dy, sobel_y, ddyy);
  280. }
  281. }} // namespace boost::gil
  282. #endif