morphology.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. //
  2. // Copyright 2021 Prathamesh Tagore <prathameshtagore@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_MORPHOLOGY_HPP
  9. #define BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP
  10. #include <boost/gil/image_processing/kernel.hpp>
  11. #include <boost/gil/gray.hpp>
  12. #include <boost/gil/image_processing/threshold.hpp>
  13. namespace boost { namespace gil { namespace detail {
  14. enum class morphological_operation
  15. {
  16. dilation,
  17. erosion,
  18. };
  19. /// \addtogroup ImageProcessing
  20. /// @{
  21. /// \brief Implements morphological operations at pixel level.This function
  22. /// compares neighbouring pixel values according to the kernel and choose
  23. /// minimum/mamximum neighbouring pixel value and assigns it to the pixel under
  24. /// consideration.
  25. /// \param src_view - Source/Input image view.
  26. /// \param dst_view - View which stores the final result of operations performed by this function.
  27. /// \param kernel - Kernel matrix/structuring element containing 0's and 1's
  28. /// which will be used for applying the required morphological operation.
  29. /// \param identifier - Indicates the type of morphological operation to be applied.
  30. /// \tparam SrcView type of source image.
  31. /// \tparam DstView type of output image.
  32. /// \tparam Kernel type of structuring element.
  33. template <typename SrcView, typename DstView, typename Kernel>
  34. void morph_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel,
  35. morphological_operation identifier)
  36. {
  37. std::ptrdiff_t flip_ker_row, flip_ker_col, row_boundary, col_boundary;
  38. typename channel_type<typename SrcView::value_type>::type target_element;
  39. for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row)
  40. {
  41. for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col)
  42. {
  43. target_element = src_view(view_col, view_row);
  44. for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row)
  45. {
  46. flip_ker_row = kernel.size() - 1 - kernel_row; // row index of flipped kernel
  47. for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col)
  48. {
  49. flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel
  50. // We ensure that we consider only those pixels which are overlapped
  51. // on a non-zero kernel_element as
  52. if (kernel.at(flip_ker_row, flip_ker_col) == 0)
  53. {
  54. continue;
  55. }
  56. // index of input signal, used for checking boundary
  57. row_boundary = view_row + (kernel.center_y() - flip_ker_row);
  58. col_boundary = view_col + (kernel.center_x() - flip_ker_col);
  59. // ignore input samples which are out of bound
  60. if (row_boundary >= 0 && row_boundary < src_view.height() &&
  61. col_boundary >= 0 && col_boundary < src_view.width())
  62. {
  63. if (identifier == morphological_operation::dilation)
  64. {
  65. target_element =
  66. (std::max)(src_view(col_boundary, row_boundary)[0], target_element);
  67. }
  68. else if (identifier == morphological_operation::erosion)
  69. {
  70. target_element =
  71. (std::min)(src_view(col_boundary, row_boundary)[0], target_element);
  72. }
  73. }
  74. }
  75. }
  76. dst_view(view_col, view_row) = target_element;
  77. }
  78. }
  79. }
  80. /// \brief Checks feasibility of the desired operation and passes parameter
  81. /// values to the function morph_impl alongwith individual channel views of the
  82. /// input image.
  83. /// \param src_view - Source/Input image view.
  84. /// \param dst_view - View which stores the final result of operations performed by this function.
  85. /// \param kernel - Kernel matrix/structuring element containing 0's and 1's
  86. /// which will be used for applying the required morphological operation.
  87. /// \param identifier - Indicates the type of morphological operation to be applied.
  88. /// \tparam SrcView type of source image.
  89. /// \tparam DstView type of output image.
  90. /// \tparam Kernel type of structuring element.
  91. template <typename SrcView, typename DstView, typename Kernel>
  92. void morph(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat,
  93. morphological_operation identifier)
  94. {
  95. BOOST_ASSERT(ker_mat.size() != 0 && src_view.dimensions() == dst_view.dimensions());
  96. gil_function_requires<ImageViewConcept<SrcView>>();
  97. gil_function_requires<MutableImageViewConcept<DstView>>();
  98. gil_function_requires<ColorSpacesCompatibleConcept<typename color_space_type<SrcView>::type,
  99. typename color_space_type<DstView>::type>>();
  100. gil::image<typename DstView::value_type> intermediate_img(src_view.dimensions());
  101. for (std::size_t i = 0; i < src_view.num_channels(); i++)
  102. {
  103. morph_impl(nth_channel_view(src_view, i), nth_channel_view(view(intermediate_img), i),
  104. ker_mat, identifier);
  105. }
  106. copy_pixels(view(intermediate_img), dst_view);
  107. }
  108. /// \brief Calculates the difference between pixel values of first image_view
  109. /// and second image_view.
  110. /// \param src_view1 - First parameter for subtraction of views.
  111. /// \param src_view2 - Second parameter for subtraction of views.
  112. /// \param diff_view - View containing result of the subtraction of second view from
  113. /// the first view.
  114. /// \tparam SrcView type of source/Input images used for subtraction.
  115. /// \tparam DiffView type of image view containing the result of subtraction.
  116. template <typename SrcView, typename DiffView>
  117. void difference_impl(SrcView const& src_view1, SrcView const& src_view2, DiffView const& diff_view)
  118. {
  119. for (std::ptrdiff_t view_row = 0; view_row < src_view1.height(); ++view_row)
  120. for (std::ptrdiff_t view_col = 0; view_col < src_view1.width(); ++view_col)
  121. diff_view(view_col, view_row) =
  122. src_view1(view_col, view_row) - src_view2(view_col, view_row);
  123. }
  124. /// \brief Passes parameter values to the function 'difference_impl' alongwith
  125. /// individual channel views of input images.
  126. /// \param src_view1 - First parameter for subtraction of views.
  127. /// \param src_view2 - Second parameter for subtraction of views.
  128. /// \param diff_view - View containing result of the subtraction of second view from the first view.
  129. /// \tparam SrcView type of source/Input images used for subtraction.
  130. /// \tparam DiffView type of image view containing the result of subtraction.
  131. template <typename SrcView, typename DiffView>
  132. void difference(SrcView const& src_view1, SrcView const& src_view2, DiffView const& diff_view)
  133. {
  134. gil_function_requires<ImageViewConcept<SrcView>>();
  135. gil_function_requires<MutableImageViewConcept<DiffView>>();
  136. gil_function_requires<ColorSpacesCompatibleConcept<
  137. typename color_space_type<SrcView>::type, typename color_space_type<DiffView>::type>>();
  138. for (std::size_t i = 0; i < src_view1.num_channels(); i++)
  139. {
  140. difference_impl(nth_channel_view(src_view1, i), nth_channel_view(src_view2, i),
  141. nth_channel_view(diff_view, i));
  142. }
  143. }
  144. } // namespace detail
  145. /// \brief Applies morphological dilation on the input image view using given
  146. /// structuring element. It gives the maximum overlapped value to the pixel
  147. /// overlapping with the center element of structuring element. \param src_view
  148. /// - Source/input image view.
  149. /// \param int_op_view - view for writing output and performing intermediate operations.
  150. /// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
  151. /// applying dilation.
  152. /// \param iterations - Specifies the number of times dilation is to be applied on the input image
  153. /// view.
  154. /// \tparam SrcView type of source image, models gil::ImageViewConcept.
  155. /// \tparam IntOpView type of output image, models gil::MutableImageViewConcept.
  156. /// \tparam Kernel type of structuring element.
  157. template <typename SrcView, typename IntOpView, typename Kernel>
  158. void dilate(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat,
  159. int iterations)
  160. {
  161. copy_pixels(src_view, int_op_view);
  162. for (int i = 0; i < iterations; ++i)
  163. morph(int_op_view, int_op_view, ker_mat, detail::morphological_operation::dilation);
  164. }
  165. /// \brief Applies morphological erosion on the input image view using given
  166. /// structuring element. It gives the minimum overlapped value to the pixel
  167. /// overlapping with the center element of structuring element.
  168. /// \param src_view - Source/input image view.
  169. /// \param int_op_view - view for writing output and performing intermediate operations.
  170. /// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
  171. /// applying erosion.
  172. /// \param iterations - Specifies the number of times erosion is to be applied on the input
  173. /// image view.
  174. /// \tparam SrcView type of source image, models gil::ImageViewConcept.
  175. /// \tparam IntOpView type of output image, models gil::MutableImageViewConcept.
  176. /// \tparam Kernel type of structuring element.
  177. template <typename SrcView, typename IntOpView, typename Kernel>
  178. void erode(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat,
  179. int iterations)
  180. {
  181. copy_pixels(src_view, int_op_view);
  182. for (int i = 0; i < iterations; ++i)
  183. morph(int_op_view, int_op_view, ker_mat, detail::morphological_operation::erosion);
  184. }
  185. /// \brief Performs erosion and then dilation on the input image view . This
  186. /// operation is utilized for removing noise from images.
  187. /// \param src_view - Source/input image view.
  188. /// \param int_op_view - view for writing output and performing intermediate operations.
  189. /// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
  190. /// applying the opening operation.
  191. /// \tparam SrcView type of source image, models gil::ImageViewConcept.
  192. /// \tparam IntOpView type of output image, models gil::MutableImageViewConcept.
  193. /// \tparam Kernel type of structuring element.
  194. template <typename SrcView, typename IntOpView, typename Kernel>
  195. void opening(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat)
  196. {
  197. erode(src_view, int_op_view, ker_mat, 1);
  198. dilate(int_op_view, int_op_view, ker_mat, 1);
  199. }
  200. /// \brief Performs dilation and then erosion on the input image view which is
  201. /// exactly opposite to the opening operation . Closing operation can be
  202. /// utilized for closing small holes inside foreground objects.
  203. /// \param src_view - Source/input image view.
  204. /// \param int_op_view - view for writing output and performing intermediate operations.
  205. /// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
  206. /// applying the closing operation.
  207. /// \tparam SrcView type of source image, models gil::ImageViewConcept.
  208. /// \tparam IntOpView type of output image, models gil::MutableImageViewConcept.
  209. /// \tparam Kernel type of structuring element.
  210. template <typename SrcView, typename IntOpView, typename Kernel>
  211. void closing(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat)
  212. {
  213. dilate(src_view, int_op_view, ker_mat, 1);
  214. erode(int_op_view, int_op_view, ker_mat, 1);
  215. }
  216. /// \brief Calculates the difference between image views generated after
  217. /// applying dilation dilation and erosion on an image . The resultant image
  218. /// will look like the outline of the object(s) present in the image.
  219. /// \param src_view - Source/input image view.
  220. /// \param dst_view - Destination view which will store the final result of morphological
  221. /// gradient operation.
  222. /// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which
  223. /// will be used for applying the morphological gradient operation.
  224. /// \tparam SrcView type of source image, models gil::ImageViewConcept.
  225. /// \tparam DstView type of output image, models gil::MutableImageViewConcept.
  226. /// \tparam Kernel type of structuring element.
  227. template <typename SrcView, typename DstView, typename Kernel>
  228. void morphological_gradient(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat)
  229. {
  230. using namespace boost::gil;
  231. gil::image<typename DstView::value_type> int_dilate(src_view.dimensions()),
  232. int_erode(src_view.dimensions());
  233. dilate(src_view, view(int_dilate), ker_mat, 1);
  234. erode(src_view, view(int_erode), ker_mat, 1);
  235. difference(view(int_dilate), view(int_erode), dst_view);
  236. }
  237. /// \brief Calculates the difference between input image view and the view
  238. /// generated by opening operation on the input image view.
  239. /// \param src_view - Source/input image view.
  240. /// \param dst_view - Destination view which will store the final result of top hat operation.
  241. /// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
  242. /// applying the top hat operation.
  243. /// \tparam SrcView type of source image, models gil::ImageViewConcept.
  244. /// \tparam DstView type of output image, models gil::MutableImageViewConcept.
  245. /// \tparam Kernel type of structuring element.
  246. template <typename SrcView, typename DstView, typename Kernel>
  247. void top_hat(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat)
  248. {
  249. using namespace boost::gil;
  250. gil::image<typename DstView::value_type> int_opening(src_view.dimensions());
  251. opening(src_view, view(int_opening), ker_mat);
  252. difference(src_view, view(int_opening), dst_view);
  253. }
  254. /// \brief Calculates the difference between closing of the input image and
  255. /// input image.
  256. /// \param src_view - Source/input image view.
  257. /// \param dst_view - Destination view which will store the final result of black hat operation.
  258. /// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's
  259. /// which will be used for applying the black hat operation.
  260. /// \tparam SrcView type of source image, models gil::ImageViewConcept.
  261. /// \tparam DstView type of output image, models gil::MutableImageViewConcept.
  262. /// \tparam Kernel type of structuring element.
  263. template <typename SrcView, typename DstView, typename Kernel>
  264. void black_hat(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat)
  265. {
  266. using namespace boost::gil;
  267. gil::image<typename DstView::value_type> int_closing(src_view.dimensions());
  268. closing(src_view, view(int_closing), ker_mat);
  269. difference(view(int_closing), src_view, dst_view);
  270. }
  271. /// @}
  272. }} // namespace boost::gil
  273. #endif // BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP