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