Boost GIL


histogram.hpp
1 //
2 // Copyright 2020 Debabrata Mandal <mandaldebabrata123@gmail.com>
3 //
4 // Distributed under the Boost Software License, Version 1.0
5 // 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_HISTOGRAM_HPP
10 #define BOOST_GIL_HISTOGRAM_HPP
11 
12 #include <boost/gil/concepts/concept_check.hpp>
13 #include <boost/gil/metafunctions.hpp>
14 #include <boost/gil/pixel.hpp>
15 
16 #include <boost/mp11.hpp>
17 #include <boost/type_traits.hpp>
18 #include <boost/functional/hash.hpp>
19 
20 #include <iostream>
21 #include <tuple>
22 #include <utility>
23 #include <vector>
24 #include <type_traits>
25 #include <map>
26 #include <unordered_map>
27 
28 namespace boost { namespace gil {
29 
38 
39 namespace detail {
40 
43 
46 template <std::size_t Index, typename... T>
47 inline typename std::enable_if<Index == sizeof...(T), void>::type
48  hash_tuple_impl(std::size_t&, std::tuple<T...> const&)
49 {
50 }
51 
54 template <std::size_t Index, typename... T>
55 inline typename std::enable_if<Index != sizeof...(T), void>::type
56  hash_tuple_impl(std::size_t& seed, std::tuple<T...> const& t)
57 {
58  boost::hash_combine(seed, std::get<Index>(t));
59  hash_tuple_impl<Index + 1>(seed, t);
60 }
61 
71 template <typename... T>
72 struct hash_tuple
73 {
74  std::size_t operator()(std::tuple<T...> const& t) const
75  {
76  std::size_t seed = 0;
77  hash_tuple_impl<0>(seed, t);
78  return seed;
79  }
80 };
81 
85 template <typename Pixel, std::size_t... I>
86 auto pixel_to_tuple(Pixel const& p, boost::mp11::index_sequence<I...>)
87  -> decltype(std::make_tuple(p[I]...))
88 {
89  return std::make_tuple(p[I]...);
90 }
91 
95 template <typename Tuple, std::size_t... I>
96 auto tuple_to_tuple(Tuple const& t, boost::mp11::index_sequence<I...>)
97  -> decltype(std::make_tuple(std::get<I>(t)...))
98 {
99  return std::make_tuple(std::get<I>(t)...);
100 }
101 
104 template <typename Tuple, std::size_t... I>
105 bool tuple_compare(Tuple const& t1, Tuple const& t2, boost::mp11::index_sequence<I...>)
106 {
107  std::array<bool, std::tuple_size<Tuple>::value> comp_list;
108  comp_list = {std::get<I>(t1) <= std::get<I>(t2)...};
109  bool comp = true;
110  for (std::size_t i = 0; i < comp_list.size(); i++)
111  {
112  comp = comp & comp_list[i];
113  }
114  return comp;
115 }
116 
122 template <typename Tuple>
123 bool tuple_compare(Tuple const& t1, Tuple const& t2)
124 {
125  std::size_t const tuple_size = std::tuple_size<Tuple>::value;
126  auto index_list = boost::mp11::make_index_sequence<tuple_size>{};
127  return tuple_compare(t1, t2, index_list);
128 }
129 
134 template <typename Tuple>
136 {
137  static constexpr Tuple min()
138  {
139  return min_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
140  }
141  static constexpr Tuple max()
142  {
143  return max_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
144  }
145 
146 private:
147  template <std::size_t... I>
148  static constexpr Tuple min_impl(boost::mp11::index_sequence<I...>)
149  {
150  return std::make_tuple(
151  std::numeric_limits<typename std::tuple_element<I, Tuple>::type>::min()...);
152  }
153 
154  template <std::size_t... I>
155  static constexpr Tuple max_impl(boost::mp11::index_sequence<I...>)
156  {
157  return std::make_tuple(
158  std::numeric_limits<typename std::tuple_element<I, Tuple>::type>::max()...);
159  }
160 };
161 
169 template <std::size_t Dimension>
170 struct filler
171 {
172  template <typename Container, typename Tuple>
173  void operator()(Container&, Tuple&, Tuple&, std::size_t)
174  {
175  }
176 };
177 
180 template <>
181 struct filler<1>
182 {
183  template <typename Container, typename Tuple>
184  void operator()(Container& hist, Tuple& lower, Tuple& upper, std::size_t bin_width = 1)
185  {
186  for (auto i = std::get<0>(lower); static_cast<std::size_t>(std::get<0>(upper) - i) >= bin_width; i += bin_width)
187  {
188  hist(i / bin_width) = 0;
189  }
190  hist(std::get<0>(upper) / bin_width) = 0;
191  }
192 };
193 
194 } //namespace detail
195 
211 template <typename... T>
212 class histogram : public std::unordered_map<std::tuple<T...>, double, detail::hash_tuple<T...>>
213 {
214  using base_t = std::unordered_map<std::tuple<T...>, double, detail::hash_tuple<T...>>;
215  using bin_t = boost::mp11::mp_list<T...>;
216  using key_t = typename base_t::key_type;
217  using mapped_t = typename base_t::mapped_type;
218  using value_t = typename base_t::value_type;
219 
220 public:
221  histogram() = default;
222 
224  static constexpr std::size_t dimension()
225  {
226  return std::tuple_size<key_t>::value;
227  }
228 
230  mapped_t& operator()(T... indices)
231  {
232  auto key = std::make_tuple(indices...);
233  std::size_t const index_dimension = std::tuple_size<std::tuple<T...>>::value;
234  std::size_t const histogram_dimension = dimension();
235  static_assert(histogram_dimension == index_dimension, "Dimensions do not match.");
236 
237  return base_t::operator[](key);
238  }
239 
242  template <typename OtherType>
243  bool equals(OtherType const& otherhist) const
244  {
245  bool check = (dimension() == otherhist.dimension());
246 
247  using other_value_t = typename OtherType::value_type;
248  std::for_each(otherhist.begin(), otherhist.end(), [&](other_value_t const& v) {
249  key_t key = key_from_tuple(v.first);
250  if (base_t::find(key) != base_t::end())
251  {
252  check = check & (base_t::at(key) == otherhist.at(v.first));
253  }
254  else
255  {
256  check = false;
257  }
258  });
259  return check;
260  }
261 
264  static constexpr bool is_pixel_compatible()
265  {
266  using bin_types = boost::mp11::mp_list<T...>;
267  return boost::mp11::mp_all_of<bin_types, std::is_arithmetic>::value;
268  }
269 
272  template <typename Tuple>
273  bool is_tuple_compatible(Tuple const&)
274  {
275  std::size_t const tuple_size = std::tuple_size<Tuple>::value;
276  std::size_t const histogram_size = dimension();
277  // TODO : Explore consequence of using if-constexpr
278  using sequence_type = typename std::conditional
279  <
280  tuple_size >= histogram_size,
281  boost::mp11::make_index_sequence<histogram_size>,
282  boost::mp11::make_index_sequence<tuple_size>
283  >::type;
284 
285  if (is_tuple_size_compatible<Tuple>())
286  return is_tuple_type_compatible<Tuple>(sequence_type{});
287  else
288  return false;
289  }
290 
293  template <std::size_t... Dimensions, typename Tuple>
294  key_t key_from_tuple(Tuple const& t) const
295  {
296  using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
297  std::size_t const index_list_size = boost::mp11::mp_size<index_list>::value;
298  std::size_t const tuple_size = std::tuple_size<Tuple>::value;
299  std::size_t const histogram_dimension = dimension();
300 
301  static_assert(
302  ((index_list_size != 0 && index_list_size == histogram_dimension) ||
303  (tuple_size == histogram_dimension)),
304  "Tuple and histogram key of different sizes");
305 
306  using new_index_list = typename std::conditional
307  <
308  index_list_size == 0,
309  boost::mp11::mp_list_c<std::size_t, 0>,
310  index_list
311  >::type;
312 
313  std::size_t const min =
314  boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;
315 
316  std::size_t const max =
317  boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;
318 
319  static_assert((0 <= min && max < tuple_size) || index_list_size == 0, "Index out of Range");
320 
321  using seq1 = boost::mp11::make_index_sequence<histogram_dimension>;
322  using seq2 = boost::mp11::index_sequence<Dimensions...>;
323  // TODO : Explore consequence of using if-constexpr
324  using sequence_type = typename std::conditional<index_list_size == 0, seq1, seq2>::type;
325 
326  auto key = detail::tuple_to_tuple(t, sequence_type{});
327  static_assert(
328  is_tuple_type_compatible<Tuple>(seq1{}),
329  "Tuple type and histogram type not compatible.");
330 
331  return make_histogram_key(key, seq1{});
332  }
333 
336  template <std::size_t... Dimensions, typename Pixel>
337  key_t key_from_pixel(Pixel const& p) const
338  {
339  using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
340  std::size_t const index_list_size = boost::mp11::mp_size<index_list>::value;
341  std::size_t const pixel_dimension = num_channels<Pixel>::value;
342  std::size_t const histogram_dimension = dimension();
343 
344  static_assert(
345  ((index_list_size != 0 && index_list_size == histogram_dimension) ||
346  (index_list_size == 0 && pixel_dimension == histogram_dimension)) &&
347  is_pixel_compatible(),
348  "Pixels and histogram key are not compatible.");
349 
350  using new_index_list = typename std::conditional
351  <
352  index_list_size == 0,
353  boost::mp11::mp_list_c<std::size_t, 0>,
354  index_list
355  >::type;
356 
357  std::size_t const min =
358  boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;
359 
360  std::size_t const max =
361  boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;
362 
363  static_assert(
364  (0 <= min && max < pixel_dimension) || index_list_size == 0, "Index out of Range");
365 
366  using seq1 = boost::mp11::make_index_sequence<histogram_dimension>;
367  using seq2 = boost::mp11::index_sequence<Dimensions...>;
368  using sequence_type = typename std::conditional<index_list_size == 0, seq1, seq2>::type;
369 
370  auto key = detail::pixel_to_tuple(p, sequence_type{});
371  return make_histogram_key(key, seq1{});
372  }
373 
375  key_t nearest_key(key_t const& k) const
376  {
377  using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
378  static_assert(
379  boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
380  "Keys are not comparable.");
381  auto nearest_k = k;
382  if (base_t::find(k) != base_t::end())
383  {
384  return nearest_k;
385  }
386  else
387  {
388  bool once = true;
389  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
390  if (v.first <= k)
391  {
392  if (once)
393  {
394  once = !once;
395  nearest_k = v.first;
396  }
397  else if (nearest_k < v.first)
398  nearest_k = v.first;
399  }
400  });
401  return nearest_k;
402  }
403  }
404 
406  template <std::size_t... Dimensions, typename SrcView>
407  void fill(
408  SrcView const& srcview,
409  std::size_t bin_width = 1,
410  bool applymask = false,
411  std::vector<std::vector<bool>> mask = {},
412  key_t lower = key_t(),
413  key_t upper = key_t(),
414  bool setlimits = false)
415  {
416  gil_function_requires<ImageViewConcept<SrcView>>();
417  using channel_t = typename channel_type<SrcView>::type;
418 
419  for (std::ptrdiff_t src_y = 0; src_y < srcview.height(); ++src_y)
420  {
421  auto src_it = srcview.row_begin(src_y);
422  for (std::ptrdiff_t src_x = 0; src_x < srcview.width(); ++src_x)
423  {
424  if (applymask && !mask[src_y][src_x])
425  continue;
426  auto scaled_px = src_it[src_x];
427  static_for_each(scaled_px, [&](channel_t& ch) {
428  ch = ch / bin_width;
429  });
430  auto key = key_from_pixel<Dimensions...>(scaled_px);
431  if (!setlimits ||
432  (detail::tuple_compare(lower, key) && detail::tuple_compare(key, upper)))
433  base_t::operator[](key)++;
434  }
435  }
436  }
437 
439  template <std::size_t... Dimensions, typename Tuple>
440  histogram sub_histogram(Tuple const& t1, Tuple const& t2)
441  {
442  using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
443  std::size_t const index_list_size = boost::mp11::mp_size<index_list>::value;
444  std::size_t const histogram_dimension = dimension();
445 
446  std::size_t const min =
447  boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;
448 
449  std::size_t const max =
450  boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;
451 
452  static_assert(
453  (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
454  "Index out of Range");
455 
456  using seq1 = boost::mp11::make_index_sequence<dimension()>;
457  using seq2 = boost::mp11::index_sequence<Dimensions...>;
458 
459  static_assert(
460  is_tuple_type_compatible<Tuple>(seq1{}),
461  "Tuple type and histogram type not compatible.");
462 
463  auto low = make_histogram_key(t1, seq1{});
464  auto low_key = detail::tuple_to_tuple(low, seq2{});
465  auto high = make_histogram_key(t2, seq1{});
466  auto high_key = detail::tuple_to_tuple(high, seq2{});
467 
468  histogram sub_h;
469  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& k) {
470  auto tmp_key = detail::tuple_to_tuple(k.first, seq2{});
471  if (low_key <= tmp_key && tmp_key <= high_key)
472  sub_h[k.first] += base_t::operator[](k.first);
473  });
474  return sub_h;
475  }
476 
478  template <std::size_t... Dimensions>
480  {
481  using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
482  std::size_t const index_list_size = boost::mp11::mp_size<index_list>::value;
483  std::size_t const histogram_dimension = dimension();
484 
485  std::size_t const min =
486  boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;
487 
488  std::size_t const max =
489  boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;
490 
491  static_assert(
492  (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
493  "Index out of Range");
494 
496 
497  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
498  auto sub_key =
499  detail::tuple_to_tuple(v.first, boost::mp11::index_sequence<Dimensions...>{});
500  sub_h[sub_key] += base_t::operator[](v.first);
501  });
502  return sub_h;
503  }
504 
506  void normalize()
507  {
508  double sum = 0.0;
509  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
510  sum += v.second;
511  });
512  // std::cout<<(long int)sum<<"asfe";
513  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
514  base_t::operator[](v.first) = v.second / sum;
515  });
516  }
517 
519  double sum() const
520  {
521  double sum = 0.0;
522  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
523  sum += v.second;
524  });
525  return sum;
526  }
527 
529  key_t min_key() const
530  {
531  key_t min_key = base_t::begin()->first;
532  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
533  if (v.first < min_key)
534  min_key = v.first;
535  });
536  return min_key;
537  }
538 
540  key_t max_key() const
541  {
542  key_t max_key = base_t::begin()->first;
543  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
544  if (v.first > max_key)
545  max_key = v.first;
546  });
547  return max_key;
548  }
549 
551  std::vector<key_t> sorted_keys() const
552  {
553  std::vector<key_t> sorted_keys;
554  std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
555  sorted_keys.push_back(v.first);
556  });
557  std::sort(sorted_keys.begin(), sorted_keys.end());
558  return sorted_keys;
559  }
560 
561 private:
562  template <typename Tuple, std::size_t... I>
563  key_t make_histogram_key(Tuple const& t, boost::mp11::index_sequence<I...>) const
564  {
565  return std::make_tuple(
566  static_cast<typename boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>>>(
567  std::get<I>(t))...);
568  }
569 
570  template <typename Tuple, std::size_t... I>
571  static constexpr bool is_tuple_type_compatible(boost::mp11::index_sequence<I...>)
572  {
573  using tp = boost::mp11::mp_list
574  <
575  typename std::is_convertible
576  <
577  boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>>,
578  typename std::tuple_element<I, Tuple>::type
579  >::type...
580  >;
581  return boost::mp11::mp_all_of<tp, boost::mp11::mp_to_bool>::value;
582  }
583 
584  template <typename Tuple>
585  static constexpr bool is_tuple_size_compatible()
586  {
587  return (std::tuple_size<Tuple>::value == dimension());
588  }
589 };
590 
605 template <typename SrcView, typename Container>
606 void fill_histogram(SrcView const&, Container&);
607 
629 template <std::size_t... Dimensions, typename SrcView, typename... T>
630 void fill_histogram(
631  SrcView const& srcview,
632  histogram<T...>& hist,
633  std::size_t bin_width = 1,
634  bool accumulate = false,
635  bool sparsefill = true,
636  bool applymask = false,
637  std::vector<std::vector<bool>> mask = {},
638  typename histogram<T...>::key_type lower =
639  detail::tuple_limit<typename histogram<T...>::key_type>::min(),
640  typename histogram<T...>::key_type upper =
641  detail::tuple_limit<typename histogram<T...>::key_type>::max(),
642  bool setlimits = false)
643 {
644  if (!accumulate)
645  hist.clear();
646 
647  detail::filler<histogram<T...>::dimension()> f;
648  if (!sparsefill)
649  f(hist, lower, upper, bin_width);
650 
651  hist.template fill<Dimensions...>(srcview, bin_width, applymask, mask, lower, upper, setlimits);
652 }
653 
666 template <typename Container>
667 Container cumulative_histogram(Container const&);
668 
669 template <typename... T>
670 histogram<T...> cumulative_histogram(histogram<T...> const& hist)
671 {
672  using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
673  static_assert(
674  boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
675  "Cumulative histogram not possible of this type");
676 
677  using histogram_t = histogram<T...>;
678  using pair_t = std::pair<typename histogram_t::key_type, typename histogram_t::mapped_type>;
679  using value_t = typename histogram_t::value_type;
680 
681  histogram_t cumulative_hist;
682  std::size_t const dims = histogram_t::dimension();
683  if (dims == 1)
684  {
685  std::vector<pair_t> sorted_keys(hist.size());
686  std::size_t counter = 0;
687  std::for_each(hist.begin(), hist.end(), [&](value_t const& v) {
688  sorted_keys[counter++] = std::make_pair(v.first, v.second);
689  });
690  std::sort(sorted_keys.begin(), sorted_keys.end());
691  auto cumulative_counter = static_cast<typename histogram_t::mapped_type>(0);
692  for (std::size_t i = 0; i < sorted_keys.size(); ++i)
693  {
694  cumulative_counter += sorted_keys[i].second;
695  cumulative_hist[(sorted_keys[i].first)] = cumulative_counter;
696  }
697  }
698  else
699  {
700  std::for_each(hist.begin(), hist.end(), [&](value_t const& v1) {
701  auto cumulative_counter = static_cast<typename histogram_t::mapped_type>(0);
702  std::for_each(hist.begin(), hist.end(), [&](value_t const& v2) {
703  bool comp = detail::tuple_compare(
704  v2.first, v1.first,
705  boost::mp11::make_index_sequence<histogram_t::dimension()>{});
706  if (comp)
707  cumulative_counter += hist.at(v2.first);
708  });
709  cumulative_hist[v1.first] = cumulative_counter;
710  });
711  }
712  return cumulative_hist;
713 }
714 
715 }} //namespace boost::gil
716 
717 #endif
boost::gil::histogram::equals
bool equals(OtherType const &otherhist) const
Checks if 2 histograms are equal. Ignores type, and checks if the keys (after type casting) match.
Definition: histogram.hpp:243
boost::gil::histogram::key_from_pixel
key_t key_from_pixel(Pixel const &p) const
Returns a histogram compatible key from the input pixel which can be directly used.
Definition: histogram.hpp:337
boost::gil::histogram::min_key
key_t min_key() const
Return the minimum key in histogram.
Definition: histogram.hpp:529
boost::gil::detail::filler
Filler is used to fill the histogram class with all values between a specified range This functor is ...
Definition: histogram.hpp:170
boost::gil::histogram::max_key
key_t max_key() const
Return the maximum key in histogram.
Definition: histogram.hpp:540
boost::gil::detail::tuple_limit
Provides equivalent of std::numeric_limits for type std::tuple tuple_limit gets called with only tupl...
Definition: histogram.hpp:135
boost::gil::histogram::is_tuple_compatible
bool is_tuple_compatible(Tuple const &)
Checks if the histogram class is compatible to be used with the specified tuple type.
Definition: histogram.hpp:273
boost::gil::detail::tuple_compare
bool tuple_compare(Tuple const &t1, Tuple const &t2)
Compares 2 tuples and outputs t1 <= t2 Comparison is not in a lexicographic manner but on every eleme...
Definition: histogram.hpp:123
boost::gil::detail::pixel_to_tuple
auto pixel_to_tuple(Pixel const &p, boost::mp11::index_sequence< I... >) -> decltype(std::make_tuple(p[I]...))
Definition: histogram.hpp:86
boost::gil::histogram::normalize
void normalize()
Normalize this histogram class.
Definition: histogram.hpp:506
boost::gil::detail::hash_tuple
Functor provided for the hashing of tuples. The following approach makes use hash_combine from boost:...
Definition: histogram.hpp:72
boost::gil::histogram::sub_histogram
histogram< boost::mp11::mp_at< bin_t, boost::mp11::mp_size_t< Dimensions > >... > sub_histogram()
Returns a sub-histogram over specified axes.
Definition: histogram.hpp:479
boost::gil::histogram::dimension
static constexpr std::size_t dimension()
Returns the number of dimensions(axes) the class supports.
Definition: histogram.hpp:224
boost::gil::histogram::sum
double sum() const
Return the sum count of all bins.
Definition: histogram.hpp:519
boost::gil::histogram::key_from_tuple
key_t key_from_tuple(Tuple const &t) const
Returns a key compatible to be used as the histogram key from the input tuple.
Definition: histogram.hpp:294
boost::gil::histogram::fill
void fill(SrcView const &srcview, std::size_t bin_width=1, bool applymask=false, std::vector< std::vector< bool >> mask={}, key_t lower=key_t(), key_t upper=key_t(), bool setlimits=false)
Fills the histogram with the input image view.
Definition: histogram.hpp:407
boost::gil::histogram::nearest_key
key_t nearest_key(key_t const &k) const
Return nearest smaller key to specified histogram key.
Definition: histogram.hpp:375
std::fill
void fill(boost::gil::iterator_from_2d< IL > first, boost::gil::iterator_from_2d< IL > last, const V &val)
std::fill(I,I,V) with I being a iterator_from_2d
Definition: algorithm.hpp:359
boost::gil::histogram::operator()
mapped_t & operator()(T... indices)
Returns bin value corresponding to specified tuple.
Definition: histogram.hpp:230
boost::gil::histogram::is_pixel_compatible
static constexpr bool is_pixel_compatible()
Checks if the histogram class is compatible to be used with a GIL image type.
Definition: histogram.hpp:264
boost::gil::histogram::sorted_keys
std::vector< key_t > sorted_keys() const
Return sorted keys in a vector.
Definition: histogram.hpp:551
boost::gil::histogram::sub_histogram
histogram sub_histogram(Tuple const &t1, Tuple const &t2)
Can return a subset or a mask over the current histogram.
Definition: histogram.hpp:440
boost::gil::channel_type
Definition: color_convert.hpp:31
boost::gil::num_channels
Returns the number of channels of a pixel-based GIL construct.
Definition: locator.hpp:38
boost::gil::histogram
Default histogram class provided by boost::gil.
Definition: histogram.hpp:212
boost::gil::detail::tuple_to_tuple
auto tuple_to_tuple(Tuple const &t, boost::mp11::index_sequence< I... >) -> decltype(std::make_tuple(std::get< I >(t)...))
Definition: histogram.hpp:96