Boost GIL


histogram_matching.hpp
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 
9 #ifndef BOOST_GIL_IMAGE_PROCESSING_HISTOGRAM_MATCHING_HPP
10 #define BOOST_GIL_IMAGE_PROCESSING_HISTOGRAM_MATCHING_HPP
11 
12 #include <boost/gil/algorithm.hpp>
13 #include <boost/gil/histogram.hpp>
14 #include <boost/gil/image.hpp>
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <map>
19 #include <vector>
20 
21 namespace boost { namespace gil {
22 
36 
45 template <typename SrcKeyType, typename RefKeyType>
46 std::map<SrcKeyType, SrcKeyType>
47  histogram_matching(histogram<SrcKeyType> const& src_hist, histogram<RefKeyType> const& ref_hist)
48 {
49  histogram<SrcKeyType> dst_hist;
50  return histogram_matching(src_hist, ref_hist, dst_hist);
51 }
52 
65 template <typename SrcKeyType, typename RefKeyType, typename DstKeyType>
66 std::map<SrcKeyType, DstKeyType> histogram_matching(
67  histogram<SrcKeyType> const& src_hist,
68  histogram<RefKeyType> const& ref_hist,
69  histogram<DstKeyType>& dst_hist)
70 {
71  static_assert(
72  std::is_integral<SrcKeyType>::value &&
73  std::is_integral<RefKeyType>::value &&
74  std::is_integral<DstKeyType>::value,
75  "Source, Refernce or Destination histogram type is not appropriate.");
76 
77  using value_t = typename histogram<SrcKeyType>::value_type;
78  dst_hist.clear();
79  double src_sum = src_hist.sum();
80  double ref_sum = ref_hist.sum();
81  auto cumltv_srchist = cumulative_histogram(src_hist);
82  auto cumltv_refhist = cumulative_histogram(ref_hist);
83  std::map<SrcKeyType, RefKeyType> inverse_mapping;
84 
85  std::vector<typename histogram<RefKeyType>::key_type> src_keys, ref_keys;
86  src_keys = src_hist.sorted_keys();
87  ref_keys = ref_hist.sorted_keys();
88  std::ptrdiff_t start = ref_keys.size() - 1;
89  RefKeyType ref_max;
90  if (start >= 0)
91  ref_max = std::get<0>(ref_keys[start]);
92 
93  for (std::ptrdiff_t j = src_keys.size() - 1; j >= 0; --j)
94  {
95  double src_val = (cumltv_srchist[src_keys[j]] * ref_sum) / src_sum;
96  while (cumltv_refhist[ref_keys[start]] > src_val && start > 0)
97  {
98  start--;
99  }
100  if (std::abs(cumltv_refhist[ref_keys[start]] - src_val) >
101  std::abs(cumltv_refhist(std::min<RefKeyType>(ref_max, std::get<0>(ref_keys[start + 1]))) -
102  src_val))
103  {
104  inverse_mapping[std::get<0>(src_keys[j])] =
105  std::min<RefKeyType>(ref_max, std::get<0>(ref_keys[start + 1]));
106  }
107  else
108  {
109  inverse_mapping[std::get<0>(src_keys[j])] = std::get<0>(ref_keys[start]);
110  }
111  if (j == 0)
112  break;
113  }
114  std::for_each(src_hist.begin(), src_hist.end(), [&](value_t const& v) {
115  dst_hist[inverse_mapping[std::get<0>(v.first)]] += v.second;
116  });
117  return inverse_mapping;
118 }
119 
133 template <typename SrcView, typename ReferenceView, typename DstView>
134 void histogram_matching(
135  SrcView const& src_view,
136  ReferenceView const& ref_view,
137  DstView const& dst_view,
138  std::size_t bin_width = 1,
139  bool mask = false,
140  std::vector<std::vector<bool>> src_mask = {},
141  std::vector<std::vector<bool>> ref_mask = {})
142 {
143  gil_function_requires<ImageViewConcept<SrcView>>();
144  gil_function_requires<ImageViewConcept<ReferenceView>>();
145  gil_function_requires<MutableImageViewConcept<DstView>>();
146 
147  static_assert(
148  color_spaces_are_compatible<
149  typename color_space_type<SrcView>::type,
150  typename color_space_type<ReferenceView>::type>::value,
151  "Source and reference view must have same color space");
152 
153  static_assert(
154  color_spaces_are_compatible<
155  typename color_space_type<SrcView>::type,
156  typename color_space_type<DstView>::type>::value,
157  "Source and destination view must have same color space");
158 
159  // Defining channel type
160  using source_channel_t = typename channel_type<SrcView>::type;
161  using ref_channel_t = typename channel_type<ReferenceView>::type;
162  using dst_channel_t = typename channel_type<DstView>::type;
163  using coord_t = typename SrcView::x_coord_t;
164 
165  std::size_t const channels = num_channels<SrcView>::value;
166  coord_t const width = src_view.width();
167  coord_t const height = src_view.height();
168  source_channel_t src_pixel_min = std::numeric_limits<source_channel_t>::min();
169  source_channel_t src_pixel_max = std::numeric_limits<source_channel_t>::max();
170  ref_channel_t ref_pixel_min = std::numeric_limits<ref_channel_t>::min();
171  ref_channel_t ref_pixel_max = std::numeric_limits<ref_channel_t>::max();
172 
173  for (std::size_t i = 0; i < channels; i++)
174  {
175  histogram<source_channel_t> src_histogram;
176  histogram<ref_channel_t> ref_histogram;
177  fill_histogram(
178  nth_channel_view(src_view, i), src_histogram, bin_width, false, false, mask, src_mask,
179  std::tuple<source_channel_t>(src_pixel_min),
180  std::tuple<source_channel_t>(src_pixel_max), true);
181  fill_histogram(
182  nth_channel_view(ref_view, i), ref_histogram, bin_width, false, false, mask, ref_mask,
183  std::tuple<ref_channel_t>(ref_pixel_min), std::tuple<ref_channel_t>(ref_pixel_max),
184  true);
185  auto inverse_mapping = histogram_matching(src_histogram, ref_histogram);
186  for (std::ptrdiff_t src_y = 0; src_y < height; ++src_y)
187  {
188  auto src_it = nth_channel_view(src_view, i).row_begin(src_y);
189  auto dst_it = nth_channel_view(dst_view, i).row_begin(src_y);
190  for (std::ptrdiff_t src_x = 0; src_x < width; ++src_x)
191  {
192  if (mask && !src_mask[src_y][src_x])
193  dst_it[src_x][0] = src_it[src_x][0];
194  else
195  dst_it[src_x][0] =
196  static_cast<dst_channel_t>(inverse_mapping[src_it[src_x][0]]);
197  }
198  }
199  }
200 }
201 
202 }} //namespace boost::gil
203 
204 #endif
boost::gil::nth_channel_view
nth_channel_view_type< View >::type nth_channel_view(const View &src, int n)
Definition: image_view_factory.hpp:418