9 #ifndef BOOST_GIL_HISTOGRAM_HPP
10 #define BOOST_GIL_HISTOGRAM_HPP
12 #include <boost/gil/concepts/concept_check.hpp>
13 #include <boost/gil/metafunctions.hpp>
14 #include <boost/gil/pixel.hpp>
16 #include <boost/mp11.hpp>
17 #include <boost/type_traits.hpp>
18 #include <boost/functional/hash.hpp>
25 #include <type_traits>
27 #include <unordered_map>
29 namespace boost {
namespace gil {
47 template <std::size_t Index,
typename... T>
48 inline typename std::enable_if<Index ==
sizeof...(T),
void>::type
49 hash_tuple_impl(std::size_t&, std::tuple<T...>
const&)
55 template <std::size_t Index,
typename... T>
56 inline typename std::enable_if<Index !=
sizeof...(T),
void>::type
57 hash_tuple_impl(std::size_t& seed, std::tuple<T...>
const& t)
59 boost::hash_combine(seed, std::get<Index>(t));
60 hash_tuple_impl<Index + 1>(seed, t);
72 template <
typename... T>
75 std::size_t operator()(std::tuple<T...>
const& t)
const
78 hash_tuple_impl<0>(seed, t);
86 template <
typename Pixel, std::size_t... I>
88 -> decltype(std::make_tuple(p[I]...))
90 return std::make_tuple(p[I]...);
96 template <
typename Tuple, std::size_t... I>
98 -> decltype(std::make_tuple(std::get<I>(t)...))
100 return std::make_tuple(std::get<I>(t)...);
105 template <
typename Tuple, std::size_t... I>
106 bool tuple_compare(Tuple
const& t1, Tuple
const& t2, boost::mp11::index_sequence<I...>)
108 std::array<bool, std::tuple_size<Tuple>::value> comp_list;
109 comp_list = {std::get<I>(t1) <= std::get<I>(t2)...};
111 for (std::size_t i = 0; i < comp_list.size(); i++)
113 comp = comp & comp_list[i];
123 template <
typename Tuple>
126 std::size_t
const tuple_size = std::tuple_size<Tuple>::value;
127 auto index_list = boost::mp11::make_index_sequence<tuple_size>{};
135 template <
typename Tuple>
138 static constexpr Tuple min()
140 return min_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
142 static constexpr Tuple max()
144 return max_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
148 template <std::size_t... I>
149 static constexpr Tuple min_impl(boost::mp11::index_sequence<I...>)
151 return std::make_tuple(
152 std::numeric_limits<
typename std::tuple_element<I, Tuple>::type>::min()...);
155 template <std::size_t... I>
156 static constexpr Tuple max_impl(boost::mp11::index_sequence<I...>)
158 return std::make_tuple(
159 std::numeric_limits<
typename std::tuple_element<I, Tuple>::type>::max()...);
170 template <std::
size_t Dimension>
173 template <
typename Container,
typename Tuple>
174 void operator()(Container&, Tuple&, Tuple&, std::size_t)
184 template <
typename Container,
typename Tuple>
185 void operator()(Container& hist, Tuple& lower, Tuple& upper, std::size_t bin_width = 1)
187 for (
auto i = std::get<0>(lower);
static_cast<std::size_t
>(std::get<0>(upper) - i) >= bin_width; i += bin_width)
189 hist(i / bin_width) = 0;
191 hist(std::get<0>(upper) / bin_width) = 0;
212 template <
typename... T>
213 class histogram :
public std::unordered_map<std::tuple<T...>, double, detail::hash_tuple<T...>>
215 using base_t = std::unordered_map<std::tuple<T...>, double,
detail::hash_tuple<T...>>;
216 using bin_t = boost::mp11::mp_list<T...>;
217 using key_t =
typename base_t::key_type;
218 using mapped_t =
typename base_t::mapped_type;
219 using value_t =
typename base_t::value_type;
227 return std::tuple_size<key_t>::value;
233 auto key = std::make_tuple(indices...);
234 std::size_t
const index_dimension = std::tuple_size<std::tuple<T...>>::value;
235 std::size_t
const histogram_dimension =
dimension();
236 static_assert(histogram_dimension == index_dimension,
"Dimensions do not match.");
238 return base_t::operator[](key);
243 template <
typename OtherType>
244 bool equals(OtherType
const& otherhist)
const
246 bool check = (
dimension() == otherhist.dimension());
248 using other_value_t =
typename OtherType::value_type;
249 std::for_each(otherhist.begin(), otherhist.end(), [&](other_value_t
const& v) {
250 key_t key = key_from_tuple(v.first);
251 if (base_t::find(key) != base_t::end())
253 check = check & (base_t::at(key) == otherhist.at(v.first));
267 using bin_types = boost::mp11::mp_list<T...>;
268 return boost::mp11::mp_all_of<bin_types, std::is_arithmetic>::value;
273 template <
typename Tuple>
276 std::size_t
const tuple_size = std::tuple_size<Tuple>::value;
277 std::size_t
const histogram_size = dimension();
279 using sequence_type =
typename std::conditional
281 tuple_size >= histogram_size,
282 boost::mp11::make_index_sequence<histogram_size>,
283 boost::mp11::make_index_sequence<tuple_size>
286 if (is_tuple_size_compatible<Tuple>())
287 return is_tuple_type_compatible<Tuple>(sequence_type{});
294 template <std::size_t... Dimensions,
typename Tuple>
297 using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
298 std::size_t
const index_list_size = boost::mp11::mp_size<index_list>::value;
299 std::size_t
const tuple_size = std::tuple_size<Tuple>::value;
300 std::size_t
const histogram_dimension = dimension();
303 ((index_list_size != 0 && index_list_size == histogram_dimension) ||
304 (tuple_size == histogram_dimension)),
305 "Tuple and histogram key of different sizes");
307 using new_index_list =
typename std::conditional
309 index_list_size == 0,
310 boost::mp11::mp_list_c<std::size_t, 0>,
314 std::size_t
const min =
315 boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;
317 std::size_t
const max =
318 boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;
320 static_assert((0 <= min && max < tuple_size) || index_list_size == 0,
"Index out of Range");
322 using seq1 = boost::mp11::make_index_sequence<histogram_dimension>;
323 using seq2 = boost::mp11::index_sequence<Dimensions...>;
325 using sequence_type =
typename std::conditional<index_list_size == 0, seq1, seq2>::type;
329 is_tuple_type_compatible<Tuple>(seq1{}),
330 "Tuple type and histogram type not compatible.");
332 return make_histogram_key(key, seq1{});
337 template <std::size_t... Dimensions,
typename Pixel>
340 using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
341 std::size_t
const index_list_size = boost::mp11::mp_size<index_list>::value;
343 std::size_t
const histogram_dimension = dimension();
346 ((index_list_size != 0 && index_list_size == histogram_dimension) ||
347 (index_list_size == 0 && pixel_dimension == histogram_dimension)) &&
348 is_pixel_compatible(),
349 "Pixels and histogram key are not compatible.");
351 using new_index_list =
typename std::conditional
353 index_list_size == 0,
354 boost::mp11::mp_list_c<std::size_t, 0>,
358 std::size_t
const min =
359 boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;
361 std::size_t
const max =
362 boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;
365 (0 <= min && max < pixel_dimension) || index_list_size == 0,
"Index out of Range");
367 using seq1 = boost::mp11::make_index_sequence<histogram_dimension>;
368 using seq2 = boost::mp11::index_sequence<Dimensions...>;
369 using sequence_type =
typename std::conditional<index_list_size == 0, seq1, seq2>::type;
372 return make_histogram_key(key, seq1{});
378 using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
380 boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
381 "Keys are not comparable.");
383 if (base_t::find(k) != base_t::end())
390 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
398 else if (nearest_k < v.first)
407 template <std::size_t... Dimensions,
typename SrcView>
409 SrcView
const& srcview,
410 std::size_t bin_width = 1,
411 bool applymask =
false,
412 std::vector<std::vector<bool>> mask = {},
413 key_t lower = key_t(),
414 key_t upper = key_t(),
415 bool setlimits =
false)
417 gil_function_requires<ImageViewConcept<SrcView>>();
420 for (std::ptrdiff_t src_y = 0; src_y < srcview.height(); ++src_y)
422 auto src_it = srcview.row_begin(src_y);
423 for (std::ptrdiff_t src_x = 0; src_x < srcview.width(); ++src_x)
425 if (applymask && !mask[src_y][src_x])
427 auto scaled_px = src_it[src_x];
428 static_for_each(scaled_px, [&](channel_t& ch) {
431 auto key = key_from_pixel<Dimensions...>(scaled_px);
434 base_t::operator[](key)++;
440 template <std::size_t... Dimensions,
typename Tuple>
443 using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
444 std::size_t
const index_list_size = boost::mp11::mp_size<index_list>::value;
445 std::size_t
const histogram_dimension = dimension();
447 std::size_t
const min =
448 boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;
450 std::size_t
const max =
451 boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;
454 (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
455 "Index out of Range");
457 using seq1 = boost::mp11::make_index_sequence<dimension()>;
458 using seq2 = boost::mp11::index_sequence<Dimensions...>;
461 is_tuple_type_compatible<Tuple>(seq1{}),
462 "Tuple type and histogram type not compatible.");
464 auto low = make_histogram_key(t1, seq1{});
466 auto high = make_histogram_key(t2, seq1{});
470 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& k) {
472 if (low_key <= tmp_key && tmp_key <= high_key)
473 sub_h[k.first] += base_t::operator[](k.first);
479 template <std::size_t... Dimensions>
482 using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
483 std::size_t
const index_list_size = boost::mp11::mp_size<index_list>::value;
484 std::size_t
const histogram_dimension = dimension();
486 std::size_t
const min =
487 boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;
489 std::size_t
const max =
490 boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;
493 (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
494 "Index out of Range");
498 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
501 sub_h[sub_key] += base_t::operator[](v.first);
510 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
514 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
515 base_t::operator[](v.first) = v.second / sum;
523 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
532 key_t min_key = base_t::begin()->first;
533 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
534 if (v.first < min_key)
543 key_t max_key = base_t::begin()->first;
544 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
545 if (v.first > max_key)
554 std::vector<key_t> sorted_keys;
555 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
556 sorted_keys.push_back(v.first);
558 std::sort(sorted_keys.begin(), sorted_keys.end());
563 template <
typename Tuple, std::size_t... I>
564 key_t make_histogram_key(Tuple
const& t, boost::mp11::index_sequence<I...>)
const
566 return std::make_tuple(
567 static_cast<typename boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>
>>(
571 template <
typename Tuple, std::size_t... I>
572 static constexpr
bool is_tuple_type_compatible(boost::mp11::index_sequence<I...>)
574 using tp = boost::mp11::mp_list
576 typename std::is_convertible
578 boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>>,
579 typename std::tuple_element<I, Tuple>::type
582 return boost::mp11::mp_all_of<tp, boost::mp11::mp_to_bool>::value;
585 template <
typename Tuple>
586 static constexpr
bool is_tuple_size_compatible()
588 return (std::tuple_size<Tuple>::value == dimension());
606 template <
typename SrcView,
typename Container>
607 void fill_histogram(SrcView
const&, Container&);
630 template <std::size_t... Dimensions,
typename SrcView,
typename... T>
632 SrcView
const& srcview,
633 histogram<T...>& hist,
634 std::size_t bin_width = 1,
635 bool accumulate =
false,
636 bool sparsefill =
true,
637 bool applymask =
false,
638 std::vector<std::vector<bool>> mask = {},
639 typename histogram<T...>::key_type lower =
640 detail::tuple_limit<
typename histogram<T...>::key_type>::min(),
641 typename histogram<T...>::key_type upper =
642 detail::tuple_limit<
typename histogram<T...>::key_type>::max(),
643 bool setlimits =
false)
648 detail::filler<histogram<T...>::dimension()> f;
650 f(hist, lower, upper, bin_width);
652 hist.template
fill<Dimensions...>(srcview, bin_width, applymask, mask, lower, upper, setlimits);
667 template <
typename Container>
668 Container cumulative_histogram(Container
const&);
670 template <
typename... T>
671 histogram<T...> cumulative_histogram(histogram<T...>
const& hist)
673 using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
675 boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
676 "Cumulative histogram not possible of this type");
678 using histogram_t = histogram<T...>;
679 using pair_t = std::pair<typename histogram_t::key_type, typename histogram_t::mapped_type>;
680 using value_t =
typename histogram_t::value_type;
682 histogram_t cumulative_hist;
683 std::size_t
const dims = histogram_t::dimension();
686 std::vector<pair_t> sorted_keys(hist.size());
687 std::size_t counter = 0;
688 std::for_each(hist.begin(), hist.end(), [&](value_t
const& v) {
689 sorted_keys[counter++] = std::make_pair(v.first, v.second);
691 std::sort(sorted_keys.begin(), sorted_keys.end());
692 auto cumulative_counter =
static_cast<typename histogram_t::mapped_type
>(0);
693 for (std::size_t i = 0; i < sorted_keys.size(); ++i)
695 cumulative_counter += sorted_keys[i].second;
696 cumulative_hist[(sorted_keys[i].first)] = cumulative_counter;
701 std::for_each(hist.begin(), hist.end(), [&](value_t
const& v1) {
702 auto cumulative_counter = static_cast<typename histogram_t::mapped_type>(0);
703 std::for_each(hist.begin(), hist.end(), [&](value_t const& v2) {
704 bool comp = detail::tuple_compare(
706 boost::mp11::make_index_sequence<histogram_t::dimension()>{});
708 cumulative_counter += hist.at(v2.first);
710 cumulative_hist[v1.first] = cumulative_counter;
713 return cumulative_hist;