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>
24 #include <type_traits>
26 #include <unordered_map>
28 namespace boost {
namespace gil {
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&)
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)
58 boost::hash_combine(seed, std::get<Index>(t));
59 hash_tuple_impl<Index + 1>(seed, t);
71 template <
typename... T>
74 std::size_t operator()(std::tuple<T...>
const& t)
const
77 hash_tuple_impl<0>(seed, t);
85 template <
typename Pixel, std::size_t... I>
87 -> decltype(std::make_tuple(p[I]...))
89 return std::make_tuple(p[I]...);
95 template <
typename Tuple, std::size_t... I>
97 -> decltype(std::make_tuple(std::get<I>(t)...))
99 return std::make_tuple(std::get<I>(t)...);
104 template <
typename Tuple, std::size_t... I>
105 bool tuple_compare(Tuple
const& t1, Tuple
const& t2, boost::mp11::index_sequence<I...>)
107 std::array<bool, std::tuple_size<Tuple>::value> comp_list;
108 comp_list = {std::get<I>(t1) <= std::get<I>(t2)...};
110 for (std::size_t i = 0; i < comp_list.size(); i++)
112 comp = comp & comp_list[i];
122 template <
typename Tuple>
125 std::size_t
const tuple_size = std::tuple_size<Tuple>::value;
126 auto index_list = boost::mp11::make_index_sequence<tuple_size>{};
134 template <
typename Tuple>
137 static constexpr Tuple min()
139 return min_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
141 static constexpr Tuple max()
143 return max_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
147 template <std::size_t... I>
148 static constexpr Tuple min_impl(boost::mp11::index_sequence<I...>)
150 return std::make_tuple(
151 std::numeric_limits<
typename std::tuple_element<I, Tuple>::type>::min()...);
154 template <std::size_t... I>
155 static constexpr Tuple max_impl(boost::mp11::index_sequence<I...>)
157 return std::make_tuple(
158 std::numeric_limits<
typename std::tuple_element<I, Tuple>::type>::max()...);
169 template <std::
size_t Dimension>
172 template <
typename Container,
typename Tuple>
173 void operator()(Container&, Tuple&, Tuple&, std::size_t)
183 template <
typename Container,
typename Tuple>
184 void operator()(Container& hist, Tuple& lower, Tuple& upper, std::size_t bin_width = 1)
186 for (
auto i = std::get<0>(lower);
static_cast<std::size_t
>(std::get<0>(upper) - i) >= bin_width; i += bin_width)
188 hist(i / bin_width) = 0;
190 hist(std::get<0>(upper) / bin_width) = 0;
211 template <
typename... T>
212 class histogram :
public std::unordered_map<std::tuple<T...>, double, detail::hash_tuple<T...>>
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;
226 return std::tuple_size<key_t>::value;
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.");
237 return base_t::operator[](key);
242 template <
typename OtherType>
243 bool equals(OtherType
const& otherhist)
const
245 bool check = (
dimension() == otherhist.dimension());
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())
252 check = check & (base_t::at(key) == otherhist.at(v.first));
266 using bin_types = boost::mp11::mp_list<T...>;
267 return boost::mp11::mp_all_of<bin_types, std::is_arithmetic>::value;
272 template <
typename Tuple>
275 std::size_t
const tuple_size = std::tuple_size<Tuple>::value;
276 std::size_t
const histogram_size = dimension();
278 using sequence_type =
typename std::conditional
280 tuple_size >= histogram_size,
281 boost::mp11::make_index_sequence<histogram_size>,
282 boost::mp11::make_index_sequence<tuple_size>
285 if (is_tuple_size_compatible<Tuple>())
286 return is_tuple_type_compatible<Tuple>(sequence_type{});
293 template <std::size_t... Dimensions,
typename Tuple>
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();
302 ((index_list_size != 0 && index_list_size == histogram_dimension) ||
303 (tuple_size == histogram_dimension)),
304 "Tuple and histogram key of different sizes");
306 using new_index_list =
typename std::conditional
308 index_list_size == 0,
309 boost::mp11::mp_list_c<std::size_t, 0>,
313 std::size_t
const min =
314 boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;
316 std::size_t
const max =
317 boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;
319 static_assert((0 <= min && max < tuple_size) || index_list_size == 0,
"Index out of Range");
321 using seq1 = boost::mp11::make_index_sequence<histogram_dimension>;
322 using seq2 = boost::mp11::index_sequence<Dimensions...>;
324 using sequence_type =
typename std::conditional<index_list_size == 0, seq1, seq2>::type;
328 is_tuple_type_compatible<Tuple>(seq1{}),
329 "Tuple type and histogram type not compatible.");
331 return make_histogram_key(key, seq1{});
336 template <std::size_t... Dimensions,
typename Pixel>
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;
342 std::size_t
const histogram_dimension = dimension();
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.");
350 using new_index_list =
typename std::conditional
352 index_list_size == 0,
353 boost::mp11::mp_list_c<std::size_t, 0>,
357 std::size_t
const min =
358 boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;
360 std::size_t
const max =
361 boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;
364 (0 <= min && max < pixel_dimension) || index_list_size == 0,
"Index out of Range");
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;
371 return make_histogram_key(key, seq1{});
377 using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
379 boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
380 "Keys are not comparable.");
382 if (base_t::find(k) != base_t::end())
389 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
397 else if (nearest_k < v.first)
406 template <std::size_t... Dimensions,
typename SrcView>
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)
416 gil_function_requires<ImageViewConcept<SrcView>>();
419 for (std::ptrdiff_t src_y = 0; src_y < srcview.height(); ++src_y)
421 auto src_it = srcview.row_begin(src_y);
422 for (std::ptrdiff_t src_x = 0; src_x < srcview.width(); ++src_x)
424 if (applymask && !mask[src_y][src_x])
426 auto scaled_px = src_it[src_x];
427 static_for_each(scaled_px, [&](channel_t& ch) {
430 auto key = key_from_pixel<Dimensions...>(scaled_px);
433 base_t::operator[](key)++;
439 template <std::size_t... Dimensions,
typename Tuple>
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();
446 std::size_t
const min =
447 boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;
449 std::size_t
const max =
450 boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;
453 (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
454 "Index out of Range");
456 using seq1 = boost::mp11::make_index_sequence<dimension()>;
457 using seq2 = boost::mp11::index_sequence<Dimensions...>;
460 is_tuple_type_compatible<Tuple>(seq1{}),
461 "Tuple type and histogram type not compatible.");
463 auto low = make_histogram_key(t1, seq1{});
465 auto high = make_histogram_key(t2, seq1{});
469 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& k) {
471 if (low_key <= tmp_key && tmp_key <= high_key)
472 sub_h[k.first] += base_t::operator[](k.first);
478 template <std::size_t... Dimensions>
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();
485 std::size_t
const min =
486 boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;
488 std::size_t
const max =
489 boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;
492 (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
493 "Index out of Range");
497 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
500 sub_h[sub_key] += base_t::operator[](v.first);
509 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
513 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
514 base_t::operator[](v.first) = v.second / sum;
522 std::for_each(base_t::begin(), base_t::end(), [&](value_t
const& v) {
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)
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)
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);
557 std::sort(sorted_keys.begin(), sorted_keys.end());
562 template <
typename Tuple, std::size_t... I>
563 key_t make_histogram_key(Tuple
const& t, boost::mp11::index_sequence<I...>)
const
565 return std::make_tuple(
566 static_cast<typename boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>
>>(
570 template <
typename Tuple, std::size_t... I>
571 static constexpr
bool is_tuple_type_compatible(boost::mp11::index_sequence<I...>)
573 using tp = boost::mp11::mp_list
575 typename std::is_convertible
577 boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>>,
578 typename std::tuple_element<I, Tuple>::type
581 return boost::mp11::mp_all_of<tp, boost::mp11::mp_to_bool>::value;
584 template <
typename Tuple>
585 static constexpr
bool is_tuple_size_compatible()
587 return (std::tuple_size<Tuple>::value == dimension());
605 template <
typename SrcView,
typename Container>
606 void fill_histogram(SrcView
const&, Container&);
629 template <std::size_t... Dimensions,
typename SrcView,
typename... T>
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)
647 detail::filler<histogram<T...>::dimension()> f;
649 f(hist, lower, upper, bin_width);
651 hist.template
fill<Dimensions...>(srcview, bin_width, applymask, mask, lower, upper, setlimits);
666 template <
typename Container>
667 Container cumulative_histogram(Container
const&);
669 template <
typename... T>
670 histogram<T...> cumulative_histogram(histogram<T...>
const& hist)
672 using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
674 boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
675 "Cumulative histogram not possible of this type");
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;
681 histogram_t cumulative_hist;
682 std::size_t
const dims = histogram_t::dimension();
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);
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)
694 cumulative_counter += sorted_keys[i].second;
695 cumulative_hist[(sorted_keys[i].first)] = cumulative_counter;
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(
705 boost::mp11::make_index_sequence<histogram_t::dimension()>{});
707 cumulative_counter += hist.at(v2.first);
709 cumulative_hist[v1.first] = cumulative_counter;
712 return cumulative_hist;