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