720 lines
26 KiB
C++
720 lines
26 KiB
C++
//
|
|
// Copyright 2020 Debabrata Mandal <mandaldebabrata123@gmail.com>
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0
|
|
// See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt
|
|
//
|
|
|
|
#ifndef BOOST_GIL_HISTOGRAM_HPP
|
|
#define BOOST_GIL_HISTOGRAM_HPP
|
|
|
|
#include <boost/gil/concepts/concept_check.hpp>
|
|
#include <boost/gil/metafunctions.hpp>
|
|
#include <boost/gil/pixel.hpp>
|
|
|
|
#include <boost/mp11.hpp>
|
|
#include <boost/type_traits.hpp>
|
|
#include <boost/functional/hash.hpp>
|
|
|
|
#include <array>
|
|
#include <iostream>
|
|
#include <tuple>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <type_traits>
|
|
#include <map>
|
|
#include <unordered_map>
|
|
|
|
namespace boost { namespace gil {
|
|
|
|
//////////////////////////////////////////////////////////
|
|
/// Histogram
|
|
//////////////////////////////////////////////////////////
|
|
/// \defgroup Histogram Histogram
|
|
/// \brief Contains description of the boost.gil.histogram class, extensions provided in place
|
|
/// of the default class, algorithms over the histogram class (both extensions and the
|
|
/// default class)
|
|
///
|
|
|
|
namespace detail {
|
|
|
|
/// \defgroup Histogram-Helpers Histogram-Helpers
|
|
/// \brief Helper implementations supporting the histogram class.
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
///
|
|
template <std::size_t Index, typename... T>
|
|
inline auto hash_tuple_impl(std::size_t&, std::tuple<T...> const&)
|
|
-> typename std::enable_if<Index == sizeof...(T), void>::type
|
|
{
|
|
// terminating case
|
|
}
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
///
|
|
template <std::size_t Index, typename... T>
|
|
inline auto hash_tuple_impl(std::size_t& seed, std::tuple<T...> const& t)
|
|
-> typename std::enable_if<Index != sizeof...(T), void>::type
|
|
{
|
|
boost::hash_combine(seed, std::get<Index>(t));
|
|
hash_tuple_impl<Index + 1>(seed, t);
|
|
}
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
/// \brief Functor provided for the hashing of tuples.
|
|
/// The following approach makes use hash_combine from
|
|
/// boost::container_hash. Although there is a direct hashing
|
|
/// available for tuples, this approach will ease adopting in
|
|
/// future to a std::hash_combine. In case std::hash extends
|
|
/// support to tuples this functor as well as the helper
|
|
/// implementation hash_tuple_impl can be removed.
|
|
///
|
|
template <typename... T>
|
|
struct hash_tuple
|
|
{
|
|
auto operator()(std::tuple<T...> const& t) const -> std::size_t
|
|
{
|
|
std::size_t seed = 0;
|
|
hash_tuple_impl<0>(seed, t);
|
|
return seed;
|
|
}
|
|
};
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
/// \todo With C++14 and using auto we don't need the decltype anymore
|
|
///
|
|
template <typename Pixel, std::size_t... I>
|
|
auto pixel_to_tuple(Pixel const& p, boost::mp11::index_sequence<I...>)
|
|
-> decltype(std::make_tuple(p[I]...))
|
|
{
|
|
return std::make_tuple(p[I]...);
|
|
}
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
/// \todo With C++14 and using auto we don't need the decltype anymore
|
|
///
|
|
template <typename Tuple, std::size_t... I>
|
|
auto tuple_to_tuple(Tuple const& t, boost::mp11::index_sequence<I...>)
|
|
-> decltype(std::make_tuple(std::get<I>(t)...))
|
|
{
|
|
return std::make_tuple(std::get<I>(t)...);
|
|
}
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
///
|
|
template <typename Tuple, std::size_t... I>
|
|
bool tuple_compare(Tuple const& t1, Tuple const& t2, boost::mp11::index_sequence<I...>)
|
|
{
|
|
std::array<bool, std::tuple_size<Tuple>::value> comp_list;
|
|
comp_list = {std::get<I>(t1) <= std::get<I>(t2)...};
|
|
bool comp = true;
|
|
for (std::size_t i = 0; i < comp_list.size(); i++)
|
|
{
|
|
comp = comp & comp_list[i];
|
|
}
|
|
return comp;
|
|
}
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
/// \brief Compares 2 tuples and outputs t1 <= t2
|
|
/// Comparison is not in a lexicographic manner but on every element of the tuple hence
|
|
/// (2, 2) > (1, 3) evaluates to false
|
|
///
|
|
template <typename Tuple>
|
|
bool tuple_compare(Tuple const& t1, Tuple const& t2)
|
|
{
|
|
std::size_t const tuple_size = std::tuple_size<Tuple>::value;
|
|
auto index_list = boost::mp11::make_index_sequence<tuple_size>{};
|
|
return tuple_compare(t1, t2, index_list);
|
|
}
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
/// \brief Provides equivalent of std::numeric_limits for type std::tuple
|
|
/// tuple_limit gets called with only tuples having integral elements
|
|
///
|
|
template <typename Tuple>
|
|
struct tuple_limit
|
|
{
|
|
static constexpr Tuple min()
|
|
{
|
|
return min_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
|
|
}
|
|
static constexpr Tuple max()
|
|
{
|
|
return max_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
|
|
}
|
|
|
|
private:
|
|
template <std::size_t... I>
|
|
static constexpr Tuple min_impl(boost::mp11::index_sequence<I...>)
|
|
{
|
|
return std::make_tuple(
|
|
std::numeric_limits<typename std::tuple_element<I, Tuple>::type>::min()...);
|
|
}
|
|
|
|
template <std::size_t... I>
|
|
static constexpr Tuple max_impl(boost::mp11::index_sequence<I...>)
|
|
{
|
|
return std::make_tuple(
|
|
std::numeric_limits<typename std::tuple_element<I, Tuple>::type>::max()...);
|
|
}
|
|
};
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
/// \brief Filler is used to fill the histogram class with all values between a specified range
|
|
/// This functor is used when sparsefill is false, since all the keys need to be present
|
|
/// in that case.
|
|
/// Currently on 1D implementation is available, extend by adding specialization for 2D
|
|
/// and higher dimensional cases.
|
|
///
|
|
template <std::size_t Dimension>
|
|
struct filler
|
|
{
|
|
template <typename Container, typename Tuple>
|
|
void operator()(Container&, Tuple&, Tuple&, std::size_t)
|
|
{
|
|
}
|
|
};
|
|
|
|
/// \ingroup Histogram-Helpers
|
|
/// \brief Specialisation for 1D histogram.
|
|
template <>
|
|
struct filler<1>
|
|
{
|
|
template <typename Container, typename Tuple>
|
|
void operator()(Container& hist, Tuple& lower, Tuple& upper, std::size_t bin_width = 1)
|
|
{
|
|
for (auto i = std::get<0>(lower); static_cast<std::size_t>(std::get<0>(upper) - i) >= bin_width; i += bin_width)
|
|
{
|
|
hist(i / bin_width) = 0;
|
|
}
|
|
hist(std::get<0>(upper) / bin_width) = 0;
|
|
}
|
|
};
|
|
|
|
} //namespace detail
|
|
|
|
///
|
|
/// \class boost::gil::histogram
|
|
/// \ingroup Histogram
|
|
/// \brief Default histogram class provided by boost::gil.
|
|
///
|
|
/// The class inherits over the std::unordered_map provided by STL. A complete example/tutorial
|
|
/// of how to use the class resides in the docs.
|
|
/// Simple calling syntax for a 3D dimensional histogram :
|
|
/// \code
|
|
/// histogram<int, int , int> h;
|
|
/// h(1, 1, 1) = 0;
|
|
/// \endcode
|
|
/// This is just a starter to what all can be achieved with it, refer to the docs for the
|
|
/// full demo.
|
|
///
|
|
template <typename... T>
|
|
class histogram : public std::unordered_map<std::tuple<T...>, double, detail::hash_tuple<T...>>
|
|
{
|
|
using base_t = std::unordered_map<std::tuple<T...>, double, detail::hash_tuple<T...>>;
|
|
using bin_t = boost::mp11::mp_list<T...>;
|
|
using key_t = typename base_t::key_type;
|
|
using mapped_t = typename base_t::mapped_type;
|
|
using value_t = typename base_t::value_type;
|
|
|
|
public:
|
|
histogram() = default;
|
|
|
|
/// \brief Returns the number of dimensions(axes) the class supports.
|
|
static constexpr std::size_t dimension()
|
|
{
|
|
return std::tuple_size<key_t>::value;
|
|
}
|
|
|
|
/// \brief Returns bin value corresponding to specified tuple
|
|
mapped_t& operator()(T... indices)
|
|
{
|
|
auto key = std::make_tuple(indices...);
|
|
std::size_t const index_dimension = std::tuple_size<std::tuple<T...>>::value;
|
|
std::size_t const histogram_dimension = dimension();
|
|
static_assert(histogram_dimension == index_dimension, "Dimensions do not match.");
|
|
|
|
return base_t::operator[](key);
|
|
}
|
|
|
|
/// \brief Checks if 2 histograms are equal. Ignores type, and checks if
|
|
/// the keys (after type casting) match.
|
|
template <typename OtherType>
|
|
bool equals(OtherType const& otherhist) const
|
|
{
|
|
bool check = (dimension() == otherhist.dimension());
|
|
|
|
using other_value_t = typename OtherType::value_type;
|
|
std::for_each(otherhist.begin(), otherhist.end(), [&](other_value_t const& v) {
|
|
key_t key = key_from_tuple(v.first);
|
|
if (base_t::find(key) != base_t::end())
|
|
{
|
|
check = check & (base_t::at(key) == otherhist.at(v.first));
|
|
}
|
|
else
|
|
{
|
|
check = false;
|
|
}
|
|
});
|
|
return check;
|
|
}
|
|
|
|
/// \brief Checks if the histogram class is compatible to be used with
|
|
/// a GIL image type
|
|
static constexpr bool is_pixel_compatible()
|
|
{
|
|
using bin_types = boost::mp11::mp_list<T...>;
|
|
return boost::mp11::mp_all_of<bin_types, std::is_arithmetic>::value;
|
|
}
|
|
|
|
/// \brief Checks if the histogram class is compatible to be used with
|
|
/// the specified tuple type
|
|
template <typename Tuple>
|
|
bool is_tuple_compatible(Tuple const&)
|
|
{
|
|
std::size_t const tuple_size = std::tuple_size<Tuple>::value;
|
|
std::size_t const histogram_size = dimension();
|
|
// TODO : Explore consequence of using if-constexpr
|
|
using sequence_type = typename std::conditional
|
|
<
|
|
tuple_size >= histogram_size,
|
|
boost::mp11::make_index_sequence<histogram_size>,
|
|
boost::mp11::make_index_sequence<tuple_size>
|
|
>::type;
|
|
|
|
if (is_tuple_size_compatible<Tuple>())
|
|
return is_tuple_type_compatible<Tuple>(sequence_type{});
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/// \brief Returns a key compatible to be used as the histogram key
|
|
/// from the input tuple
|
|
template <std::size_t... Dimensions, typename Tuple>
|
|
key_t key_from_tuple(Tuple const& t) const
|
|
{
|
|
using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
|
|
std::size_t const index_list_size = boost::mp11::mp_size<index_list>::value;
|
|
std::size_t const tuple_size = std::tuple_size<Tuple>::value;
|
|
std::size_t const histogram_dimension = dimension();
|
|
|
|
static_assert(
|
|
((index_list_size != 0 && index_list_size == histogram_dimension) ||
|
|
(tuple_size == histogram_dimension)),
|
|
"Tuple and histogram key of different sizes");
|
|
|
|
using new_index_list = typename std::conditional
|
|
<
|
|
index_list_size == 0,
|
|
boost::mp11::mp_list_c<std::size_t, 0>,
|
|
index_list
|
|
>::type;
|
|
|
|
std::size_t const min =
|
|
boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;
|
|
|
|
std::size_t const max =
|
|
boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;
|
|
|
|
static_assert((0 <= min && max < tuple_size) || index_list_size == 0, "Index out of Range");
|
|
|
|
using seq1 = boost::mp11::make_index_sequence<histogram_dimension>;
|
|
using seq2 = boost::mp11::index_sequence<Dimensions...>;
|
|
// TODO : Explore consequence of using if-constexpr
|
|
using sequence_type = typename std::conditional<index_list_size == 0, seq1, seq2>::type;
|
|
|
|
auto key = detail::tuple_to_tuple(t, sequence_type{});
|
|
static_assert(
|
|
is_tuple_type_compatible<Tuple>(seq1{}),
|
|
"Tuple type and histogram type not compatible.");
|
|
|
|
return make_histogram_key(key, seq1{});
|
|
}
|
|
|
|
/// \brief Returns a histogram compatible key from the input pixel which
|
|
/// can be directly used
|
|
template <std::size_t... Dimensions, typename Pixel>
|
|
key_t key_from_pixel(Pixel const& p) const
|
|
{
|
|
using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
|
|
std::size_t const index_list_size = boost::mp11::mp_size<index_list>::value;
|
|
std::size_t const pixel_dimension = num_channels<Pixel>::value;
|
|
std::size_t const histogram_dimension = dimension();
|
|
|
|
static_assert(
|
|
((index_list_size != 0 && index_list_size == histogram_dimension) ||
|
|
(index_list_size == 0 && pixel_dimension == histogram_dimension)) &&
|
|
is_pixel_compatible(),
|
|
"Pixels and histogram key are not compatible.");
|
|
|
|
using new_index_list = typename std::conditional
|
|
<
|
|
index_list_size == 0,
|
|
boost::mp11::mp_list_c<std::size_t, 0>,
|
|
index_list
|
|
>::type;
|
|
|
|
std::size_t const min =
|
|
boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;
|
|
|
|
std::size_t const max =
|
|
boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;
|
|
|
|
static_assert(
|
|
(0 <= min && max < pixel_dimension) || index_list_size == 0, "Index out of Range");
|
|
|
|
using seq1 = boost::mp11::make_index_sequence<histogram_dimension>;
|
|
using seq2 = boost::mp11::index_sequence<Dimensions...>;
|
|
using sequence_type = typename std::conditional<index_list_size == 0, seq1, seq2>::type;
|
|
|
|
auto key = detail::pixel_to_tuple(p, sequence_type{});
|
|
return make_histogram_key(key, seq1{});
|
|
}
|
|
|
|
/// \brief Return nearest smaller key to specified histogram key
|
|
key_t nearest_key(key_t const& k) const
|
|
{
|
|
using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
|
|
static_assert(
|
|
boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
|
|
"Keys are not comparable.");
|
|
auto nearest_k = k;
|
|
if (base_t::find(k) != base_t::end())
|
|
{
|
|
return nearest_k;
|
|
}
|
|
else
|
|
{
|
|
bool once = true;
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
|
|
if (v.first <= k)
|
|
{
|
|
if (once)
|
|
{
|
|
once = !once;
|
|
nearest_k = v.first;
|
|
}
|
|
else if (nearest_k < v.first)
|
|
nearest_k = v.first;
|
|
}
|
|
});
|
|
return nearest_k;
|
|
}
|
|
}
|
|
|
|
/// \brief Fills the histogram with the input image view
|
|
template <std::size_t... Dimensions, typename SrcView>
|
|
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)
|
|
{
|
|
gil_function_requires<ImageViewConcept<SrcView>>();
|
|
using channel_t = typename channel_type<SrcView>::type;
|
|
|
|
for (std::ptrdiff_t src_y = 0; src_y < srcview.height(); ++src_y)
|
|
{
|
|
auto src_it = srcview.row_begin(src_y);
|
|
for (std::ptrdiff_t src_x = 0; src_x < srcview.width(); ++src_x)
|
|
{
|
|
if (applymask && !mask[src_y][src_x])
|
|
continue;
|
|
auto scaled_px = src_it[src_x];
|
|
static_for_each(scaled_px, [&](channel_t& ch) {
|
|
ch = ch / bin_width;
|
|
});
|
|
auto key = key_from_pixel<Dimensions...>(scaled_px);
|
|
if (!setlimits ||
|
|
(detail::tuple_compare(lower, key) && detail::tuple_compare(key, upper)))
|
|
base_t::operator[](key)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// \brief Can return a subset or a mask over the current histogram
|
|
template <std::size_t... Dimensions, typename Tuple>
|
|
histogram sub_histogram(Tuple const& t1, Tuple const& t2)
|
|
{
|
|
using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
|
|
std::size_t const index_list_size = boost::mp11::mp_size<index_list>::value;
|
|
std::size_t const histogram_dimension = dimension();
|
|
|
|
std::size_t const min =
|
|
boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;
|
|
|
|
std::size_t const max =
|
|
boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;
|
|
|
|
static_assert(
|
|
(0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
|
|
"Index out of Range");
|
|
|
|
using seq1 = boost::mp11::make_index_sequence<dimension()>;
|
|
using seq2 = boost::mp11::index_sequence<Dimensions...>;
|
|
|
|
static_assert(
|
|
is_tuple_type_compatible<Tuple>(seq1{}),
|
|
"Tuple type and histogram type not compatible.");
|
|
|
|
auto low = make_histogram_key(t1, seq1{});
|
|
auto low_key = detail::tuple_to_tuple(low, seq2{});
|
|
auto high = make_histogram_key(t2, seq1{});
|
|
auto high_key = detail::tuple_to_tuple(high, seq2{});
|
|
|
|
histogram sub_h;
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& k) {
|
|
auto tmp_key = detail::tuple_to_tuple(k.first, seq2{});
|
|
if (low_key <= tmp_key && tmp_key <= high_key)
|
|
sub_h[k.first] += base_t::operator[](k.first);
|
|
});
|
|
return sub_h;
|
|
}
|
|
|
|
/// \brief Returns a sub-histogram over specified axes
|
|
template <std::size_t... Dimensions>
|
|
histogram<boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<Dimensions>>...> sub_histogram()
|
|
{
|
|
using index_list = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
|
|
std::size_t const index_list_size = boost::mp11::mp_size<index_list>::value;
|
|
std::size_t const histogram_dimension = dimension();
|
|
|
|
std::size_t const min =
|
|
boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;
|
|
|
|
std::size_t const max =
|
|
boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;
|
|
|
|
static_assert(
|
|
(0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
|
|
"Index out of Range");
|
|
|
|
histogram<boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<Dimensions>>...> sub_h;
|
|
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
|
|
auto sub_key =
|
|
detail::tuple_to_tuple(v.first, boost::mp11::index_sequence<Dimensions...>{});
|
|
sub_h[sub_key] += base_t::operator[](v.first);
|
|
});
|
|
return sub_h;
|
|
}
|
|
|
|
/// \brief Normalize this histogram class
|
|
void normalize()
|
|
{
|
|
double sum = 0.0;
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
|
|
sum += v.second;
|
|
});
|
|
// std::cout<<(long int)sum<<"asfe";
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
|
|
base_t::operator[](v.first) = v.second / sum;
|
|
});
|
|
}
|
|
|
|
/// \brief Return the sum count of all bins
|
|
double sum() const
|
|
{
|
|
double sum = 0.0;
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
|
|
sum += v.second;
|
|
});
|
|
return sum;
|
|
}
|
|
|
|
/// \brief Return the minimum key in histogram
|
|
key_t min_key() const
|
|
{
|
|
key_t min_key = base_t::begin()->first;
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
|
|
if (v.first < min_key)
|
|
min_key = v.first;
|
|
});
|
|
return min_key;
|
|
}
|
|
|
|
/// \brief Return the maximum key in histogram
|
|
key_t max_key() const
|
|
{
|
|
key_t max_key = base_t::begin()->first;
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
|
|
if (v.first > max_key)
|
|
max_key = v.first;
|
|
});
|
|
return max_key;
|
|
}
|
|
|
|
/// \brief Return sorted keys in a vector
|
|
std::vector<key_t> sorted_keys() const
|
|
{
|
|
std::vector<key_t> sorted_keys;
|
|
std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
|
|
sorted_keys.push_back(v.first);
|
|
});
|
|
std::sort(sorted_keys.begin(), sorted_keys.end());
|
|
return sorted_keys;
|
|
}
|
|
|
|
private:
|
|
template <typename Tuple, std::size_t... I>
|
|
key_t make_histogram_key(Tuple const& t, boost::mp11::index_sequence<I...>) const
|
|
{
|
|
return std::make_tuple(
|
|
static_cast<typename boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>>>(
|
|
std::get<I>(t))...);
|
|
}
|
|
|
|
template <typename Tuple, std::size_t... I>
|
|
static constexpr bool is_tuple_type_compatible(boost::mp11::index_sequence<I...>)
|
|
{
|
|
using tp = boost::mp11::mp_list
|
|
<
|
|
typename std::is_convertible
|
|
<
|
|
boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>>,
|
|
typename std::tuple_element<I, Tuple>::type
|
|
>::type...
|
|
>;
|
|
return boost::mp11::mp_all_of<tp, boost::mp11::mp_to_bool>::value;
|
|
}
|
|
|
|
template <typename Tuple>
|
|
static constexpr bool is_tuple_size_compatible()
|
|
{
|
|
return (std::tuple_size<Tuple>::value == dimension());
|
|
}
|
|
};
|
|
|
|
///
|
|
/// \fn void fill_histogram
|
|
/// \ingroup Histogram Algorithms
|
|
/// \tparam SrcView Input image view
|
|
/// \tparam Container Input histogram container
|
|
/// \brief Overload this function to provide support for boost::gil::histogram or
|
|
/// any other external histogram
|
|
///
|
|
/// Example :
|
|
/// \code
|
|
/// histogram<int> h;
|
|
/// fill_histogram(view(img), h);
|
|
/// \endcode
|
|
///
|
|
template <typename SrcView, typename Container>
|
|
void fill_histogram(SrcView const&, Container&);
|
|
|
|
///
|
|
/// \fn void fill_histogram
|
|
/// \ingroup Histogram Algorithms
|
|
/// @param srcview Input Input image view
|
|
/// @param hist Output Histogram to be filled
|
|
/// @param bin_width Input Specify the bin widths for the histogram.
|
|
/// @param accumulate Input Specify whether to accumulate over the values already present in h (default = false)
|
|
/// @param sparsaefill Input Specify whether to have a sparse or continuous histogram (default = true)
|
|
/// @param applymask Input Specify if image mask is to be specified
|
|
/// @param mask Input Mask as a 2D vector. Used only if prev argument specified
|
|
/// @param lower Input Lower limit on the values in histogram (default numeric_limit::min() on axes)
|
|
/// @param upper Input Upper limit on the values in histogram (default numeric_limit::max() on axes)
|
|
/// @param setlimits Input Use specified limits if this is true (default is false)
|
|
/// \brief Overload version of fill_histogram
|
|
///
|
|
/// Takes a third argument to determine whether to clear container before filling.
|
|
/// For eg, when there is a need to accumulate the histograms do
|
|
/// \code
|
|
/// fill_histogram(view(img), hist, true);
|
|
/// \endcode
|
|
///
|
|
template <std::size_t... Dimensions, typename SrcView, typename... T>
|
|
void fill_histogram(
|
|
SrcView const& srcview,
|
|
histogram<T...>& hist,
|
|
std::size_t bin_width = 1,
|
|
bool accumulate = false,
|
|
bool sparsefill = true,
|
|
bool applymask = false,
|
|
std::vector<std::vector<bool>> mask = {},
|
|
typename histogram<T...>::key_type lower =
|
|
detail::tuple_limit<typename histogram<T...>::key_type>::min(),
|
|
typename histogram<T...>::key_type upper =
|
|
detail::tuple_limit<typename histogram<T...>::key_type>::max(),
|
|
bool setlimits = false)
|
|
{
|
|
if (!accumulate)
|
|
hist.clear();
|
|
|
|
detail::filler<histogram<T...>::dimension()> f;
|
|
if (!sparsefill)
|
|
f(hist, lower, upper, bin_width);
|
|
|
|
hist.template fill<Dimensions...>(srcview, bin_width, applymask, mask, lower, upper, setlimits);
|
|
}
|
|
|
|
///
|
|
/// \fn void cumulative_histogram(Container&)
|
|
/// \ingroup Histogram Algorithms
|
|
/// \tparam Container Input histogram container
|
|
/// \brief Optionally overload this function with any external histogram class
|
|
///
|
|
/// Cumulative histogram is calculated over any arbitrary dimensional
|
|
/// histogram. The only tradeoff could be the runtime complexity which in
|
|
/// the worst case would be max( #pixel_values , #bins ) * #dimensions.
|
|
/// For single dimensional histograms the complexity has been brought down to
|
|
/// #bins * log( #bins ) by sorting the keys and then calculating the cumulative version.
|
|
///
|
|
template <typename Container>
|
|
auto cumulative_histogram(Container const&) -> Container;
|
|
|
|
template <typename... T>
|
|
auto cumulative_histogram(histogram<T...> const& hist) -> histogram<T...>
|
|
{
|
|
using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
|
|
static_assert(
|
|
boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
|
|
"Cumulative histogram not possible of this type");
|
|
|
|
using histogram_t = histogram<T...>;
|
|
using pair_t = std::pair<typename histogram_t::key_type, typename histogram_t::mapped_type>;
|
|
using value_t = typename histogram_t::value_type;
|
|
|
|
histogram_t cumulative_hist;
|
|
std::size_t const dims = histogram_t::dimension();
|
|
if (dims == 1)
|
|
{
|
|
std::vector<pair_t> sorted_keys(hist.size());
|
|
std::size_t counter = 0;
|
|
std::for_each(hist.begin(), hist.end(), [&](value_t const& v) {
|
|
sorted_keys[counter++] = std::make_pair(v.first, v.second);
|
|
});
|
|
std::sort(sorted_keys.begin(), sorted_keys.end());
|
|
auto cumulative_counter = static_cast<typename histogram_t::mapped_type>(0);
|
|
for (std::size_t i = 0; i < sorted_keys.size(); ++i)
|
|
{
|
|
cumulative_counter += sorted_keys[i].second;
|
|
cumulative_hist[(sorted_keys[i].first)] = cumulative_counter;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::for_each(hist.begin(), hist.end(), [&](value_t const& v1) {
|
|
auto cumulative_counter = static_cast<typename histogram_t::mapped_type>(0);
|
|
std::for_each(hist.begin(), hist.end(), [&](value_t const& v2) {
|
|
bool comp = detail::tuple_compare(
|
|
v2.first, v1.first,
|
|
boost::mp11::make_index_sequence<histogram_t::dimension()>{});
|
|
if (comp)
|
|
cumulative_counter += hist.at(v2.first);
|
|
});
|
|
cumulative_hist[v1.first] = cumulative_counter;
|
|
});
|
|
}
|
|
return cumulative_hist;
|
|
}
|
|
|
|
}} //namespace boost::gil
|
|
|
|
#endif
|