histogram_matching.hpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. //
  2. // Copyright 2020 Debabrata Mandal <mandaldebabrata123@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_HISTOGRAM_MATCHING_HPP
  9. #define BOOST_GIL_IMAGE_PROCESSING_HISTOGRAM_MATCHING_HPP
  10. #include <boost/gil/algorithm.hpp>
  11. #include <boost/gil/histogram.hpp>
  12. #include <boost/gil/image.hpp>
  13. #include <algorithm>
  14. #include <cmath>
  15. #include <map>
  16. #include <vector>
  17. namespace boost { namespace gil {
  18. /////////////////////////////////////////
  19. /// Histogram Matching(HM)
  20. /////////////////////////////////////////
  21. /// \defgroup HM HM
  22. /// \brief Contains implementation and description of the algorithm used to compute
  23. /// global histogram matching of input images.
  24. ///
  25. /// Algorithm :-
  26. /// 1. Calculate histogram A(pixel) of input image and G(pixel) of reference image.
  27. /// 2. Compute the normalized cumulative(CDF) histograms of A and G.
  28. /// 3. Match the histograms using transofrmation => CDF(A(px)) = CDF(G(px'))
  29. /// => px' = Inv-CDF (CDF(px))
  30. ///
  31. /// \fn histogram_matching
  32. /// \ingroup HM
  33. /// \tparam SrcKeyType Key Type of input histogram
  34. /// @param src_hist INPUT Input source histogram
  35. /// @param ref_hist INPUT Input reference histogram
  36. /// \brief Overload for histogram matching algorithm, takes in a single source histogram &
  37. /// reference histogram and returns the color map used for histogram matching.
  38. ///
  39. template <typename SrcKeyType, typename RefKeyType>
  40. auto histogram_matching(histogram<SrcKeyType> const& src_hist, histogram<RefKeyType> const& ref_hist)
  41. -> std::map<SrcKeyType, SrcKeyType>
  42. {
  43. histogram<SrcKeyType> dst_hist;
  44. return histogram_matching(src_hist, ref_hist, dst_hist);
  45. }
  46. /// \overload histogram_matching
  47. /// \ingroup HM
  48. /// \tparam SrcKeyType Key Type of input histogram
  49. /// \tparam RefKeyType Key Type of reference histogram
  50. /// \tparam DstKeyType Key Type of output histogram
  51. /// @param src_hist INPUT source histogram
  52. /// @param ref_hist INPUT reference histogram
  53. /// @param dst_hist OUTPUT Output histogram
  54. /// \brief Overload for histogram matching algorithm, takes in source histogram, reference
  55. /// histogram & destination histogram and returns the color map used for histogram
  56. /// matching as well as transforming the destination histogram.
  57. ///
  58. template <typename SrcKeyType, typename RefKeyType, typename DstKeyType>
  59. auto histogram_matching(
  60. histogram<SrcKeyType> const& src_hist,
  61. histogram<RefKeyType> const& ref_hist,
  62. histogram<DstKeyType>& dst_hist)
  63. -> std::map<SrcKeyType, DstKeyType>
  64. {
  65. static_assert(
  66. std::is_integral<SrcKeyType>::value &&
  67. std::is_integral<RefKeyType>::value &&
  68. std::is_integral<DstKeyType>::value,
  69. "Source, Refernce or Destination histogram type is not appropriate.");
  70. using value_t = typename histogram<SrcKeyType>::value_type;
  71. dst_hist.clear();
  72. double src_sum = src_hist.sum();
  73. double ref_sum = ref_hist.sum();
  74. auto cumltv_srchist = cumulative_histogram(src_hist);
  75. auto cumltv_refhist = cumulative_histogram(ref_hist);
  76. std::map<SrcKeyType, RefKeyType> inverse_mapping;
  77. std::vector<typename histogram<RefKeyType>::key_type> src_keys, ref_keys;
  78. src_keys = src_hist.sorted_keys();
  79. ref_keys = ref_hist.sorted_keys();
  80. std::ptrdiff_t start = ref_keys.size() - 1;
  81. RefKeyType ref_max;
  82. if (start >= 0)
  83. ref_max = std::get<0>(ref_keys[start]);
  84. for (std::ptrdiff_t j = src_keys.size() - 1; j >= 0; --j)
  85. {
  86. double src_val = (cumltv_srchist[src_keys[j]] * ref_sum) / src_sum;
  87. while (cumltv_refhist[ref_keys[start]] > src_val && start > 0)
  88. {
  89. start--;
  90. }
  91. if (std::abs(cumltv_refhist[ref_keys[start]] - src_val) >
  92. std::abs(cumltv_refhist(std::min<RefKeyType>(ref_max, std::get<0>(ref_keys[start + 1]))) -
  93. src_val))
  94. {
  95. inverse_mapping[std::get<0>(src_keys[j])] =
  96. std::min<RefKeyType>(ref_max, std::get<0>(ref_keys[start + 1]));
  97. }
  98. else
  99. {
  100. inverse_mapping[std::get<0>(src_keys[j])] = std::get<0>(ref_keys[start]);
  101. }
  102. if (j == 0)
  103. break;
  104. }
  105. std::for_each(src_hist.begin(), src_hist.end(), [&](value_t const& v) {
  106. dst_hist[inverse_mapping[std::get<0>(v.first)]] += v.second;
  107. });
  108. return inverse_mapping;
  109. }
  110. /// \overload histogram_matching
  111. /// \ingroup HM
  112. /// @param src_view INPUT source image view
  113. /// @param ref_view INPUT Reference image view
  114. /// @param dst_view OUTPUT Output image view
  115. /// @param bin_width INPUT Histogram bin width
  116. /// @param mask INPUT Specify is mask is to be used
  117. /// @param src_mask INPUT Mask vector over input image
  118. /// @param ref_mask INPUT Mask vector over reference image
  119. /// \brief Overload for histogram matching algorithm, takes in both source, reference &
  120. /// destination image views and histogram matches the input image using the
  121. /// reference image.
  122. ///
  123. template <typename SrcView, typename ReferenceView, typename DstView>
  124. void histogram_matching(
  125. SrcView const& src_view,
  126. ReferenceView const& ref_view,
  127. DstView const& dst_view,
  128. std::size_t bin_width = 1,
  129. bool mask = false,
  130. std::vector<std::vector<bool>> src_mask = {},
  131. std::vector<std::vector<bool>> ref_mask = {})
  132. {
  133. gil_function_requires<ImageViewConcept<SrcView>>();
  134. gil_function_requires<ImageViewConcept<ReferenceView>>();
  135. gil_function_requires<MutableImageViewConcept<DstView>>();
  136. static_assert(
  137. color_spaces_are_compatible<
  138. typename color_space_type<SrcView>::type,
  139. typename color_space_type<ReferenceView>::type>::value,
  140. "Source and reference view must have same color space");
  141. static_assert(
  142. color_spaces_are_compatible<
  143. typename color_space_type<SrcView>::type,
  144. typename color_space_type<DstView>::type>::value,
  145. "Source and destination view must have same color space");
  146. // Defining channel type
  147. using source_channel_t = typename channel_type<SrcView>::type;
  148. using ref_channel_t = typename channel_type<ReferenceView>::type;
  149. using dst_channel_t = typename channel_type<DstView>::type;
  150. using coord_t = typename SrcView::x_coord_t;
  151. std::size_t const channels = num_channels<SrcView>::value;
  152. coord_t const width = src_view.width();
  153. coord_t const height = src_view.height();
  154. source_channel_t src_pixel_min = (std::numeric_limits<source_channel_t>::min)();
  155. source_channel_t src_pixel_max = (std::numeric_limits<source_channel_t>::max)();
  156. ref_channel_t ref_pixel_min = (std::numeric_limits<ref_channel_t>::min)();
  157. ref_channel_t ref_pixel_max = (std::numeric_limits<ref_channel_t>::max)();
  158. for (std::size_t i = 0; i < channels; i++)
  159. {
  160. histogram<source_channel_t> src_histogram;
  161. histogram<ref_channel_t> ref_histogram;
  162. fill_histogram(
  163. nth_channel_view(src_view, i), src_histogram, bin_width, false, false, mask, src_mask,
  164. std::tuple<source_channel_t>(src_pixel_min),
  165. std::tuple<source_channel_t>(src_pixel_max), true);
  166. fill_histogram(
  167. nth_channel_view(ref_view, i), ref_histogram, bin_width, false, false, mask, ref_mask,
  168. std::tuple<ref_channel_t>(ref_pixel_min), std::tuple<ref_channel_t>(ref_pixel_max),
  169. true);
  170. auto inverse_mapping = histogram_matching(src_histogram, ref_histogram);
  171. for (std::ptrdiff_t src_y = 0; src_y < height; ++src_y)
  172. {
  173. auto src_it = nth_channel_view(src_view, i).row_begin(src_y);
  174. auto dst_it = nth_channel_view(dst_view, i).row_begin(src_y);
  175. for (std::ptrdiff_t src_x = 0; src_x < width; ++src_x)
  176. {
  177. if (mask && !src_mask[src_y][src_x])
  178. dst_it[src_x][0] = src_it[src_x][0];
  179. else
  180. dst_it[src_x][0] =
  181. static_cast<dst_channel_t>(inverse_mapping[src_it[src_x][0]]);
  182. }
  183. }
  184. }
  185. }
  186. }} //namespace boost::gil
  187. #endif