Skip to content

Commit

Permalink
Add histogram class and related functionality (#499)
Browse files Browse the repository at this point in the history
A new histogram class proposed with close suport for gil
image constructs.

Shift the stl support implmentation to extension to
serve as example for overloading fill_histogram.

Add cumulative histogram and histogram normalization.

Co-authored-by: debabrata1 <debabrata@goodhealthapp.com>
  • Loading branch information
codejaeger and debabrata1 authored Jan 23, 2021
1 parent 0e372a1 commit 3e729e5
Show file tree
Hide file tree
Showing 26 changed files with 2,244 additions and 34 deletions.
1 change: 1 addition & 0 deletions example/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ local sources =
resize.cpp
sobel_scharr.cpp
threshold.cpp
tutorial_histogram.cpp
x_gradient.cpp
;

Expand Down
71 changes: 37 additions & 34 deletions example/histogram.cpp
Original file line number Diff line number Diff line change
@@ -1,49 +1,52 @@
//
// Copyright 2005-2007 Adobe Systems Incorporated
// 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
//
#include <boost/gil.hpp>
#include <boost/gil/extension/io/jpeg.hpp>

#include <algorithm>
#include <fstream>
#include <boost/gil.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <boost/gil/io/read_image.hpp>

// Example file to demonstrate a way to compute histogram
#include <iostream>

using namespace boost::gil;

template <typename GrayView, typename R>
void gray_image_hist(GrayView const& img_view, R& hist)
{
for (auto it = img_view.begin(); it != img_view.end(); ++it)
++hist[*it];

// Alternatively, prefer the algorithm with lambda
// for_each_pixel(img_view, [&hist](gray8_pixel_t const& pixel) {
// ++hist[pixel];
// });
}

template <typename V, typename R>
void get_hist(const V& img_view, R& hist) {
gray_image_hist(color_converted_view<gray8_pixel_t>(img_view), hist);
}

int main() {
rgb8_image_t img;
read_image("test.jpg", img, jpeg_tag());
/*
This file explains how to use the histogram class and some of its features
that can be applied for a variety of tasks.
*/

int histogram[256];
std::fill(histogram,histogram + 256, 0);
get_hist(const_view(img), histogram);

std::fstream histo_file("out-histogram.txt", std::ios::out);
for(std::size_t ii = 0; ii < 256; ++ii)
histo_file << histogram[ii] << std::endl;
histo_file.close();
int main()
{
// Create a histogram class. Use uint or unsigned short as the default axes type in most cases.
histogram<unsigned char> h;

// Fill histogram with GIL images (of any color space)
gray8_image_t g;
read_image("test_adaptive.png", g, png_tag{});

fill_histogram
(
view(g), // Input image view
h, // Histogram to be filled
1, // Histogram bin widths
false, // Specify whether to accumulate over the values already present in h (default = false)
true, // Specify whether to have a sparse or continuous histogram (default = true)
false, // Specify if image mask is to be specified
{{}}, // Mask as a 2D vector. Used only if prev argument specified
{0}, // Lower limit on the values in histogram (default numeric_limit::min() on axes)
{255}, // Upper limit on the values in histogram (default numeric_limit::max() on axes)
true // Use specified limits if this is true (default is false)
);

// Normalize the histogram
h.normalize();

// Get a cumulative histogram from the histogram
auto h2 = cumulative_histogram(h);

return 0;
}
49 changes: 49 additions & 0 deletions example/tutorial_histogram.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// Copyright 2005-2007 Adobe Systems Incorporated
//
// 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
//
#include <boost/gil.hpp>
#include <boost/gil/extension/io/jpeg.hpp>

#include <algorithm>
#include <fstream>

// Example file to demonstrate a way to compute histogram

using namespace boost::gil;

template <typename GrayView, typename R>
void gray_image_hist(GrayView const& img_view, R& hist)
{
for (auto it = img_view.begin(); it != img_view.end(); ++it)
++hist[*it];

// Alternatively, prefer the algorithm with lambda
// for_each_pixel(img_view, [&hist](gray8_pixel_t const& pixel) {
// ++hist[pixel];
// });
}

template <typename V, typename R>
void get_hist(const V& img_view, R& hist) {
gray_image_hist(color_converted_view<gray8_pixel_t>(img_view), hist);
}

int main() {
rgb8_image_t img;
read_image("test.jpg", img, jpeg_tag());

int histogram[256];
std::fill(histogram,histogram + 256, 0);
get_hist(const_view(img), histogram);

std::fstream histo_file("out-histogram.txt", std::ios::out);
for(std::size_t ii = 0; ii < 256; ++ii)
histo_file << histogram[ii] << std::endl;
histo_file.close();

return 0;
}
1 change: 1 addition & 0 deletions include/boost/gil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <boost/gil/deprecated.hpp>
#include <boost/gil/device_n.hpp>
#include <boost/gil/gray.hpp>
#include <boost/gil/histogram.hpp>
#include <boost/gil/image.hpp>
#include <boost/gil/image_view.hpp>
#include <boost/gil/image_view_factory.hpp>
Expand Down
172 changes: 172 additions & 0 deletions include/boost/gil/extension/histogram/std.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//
// 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_EXTENSION_HISTOGRAM_STL_HISTOGRAM_HPP
#define BOOST_GIL_EXTENSION_HISTOGRAM_STL_HISTOGRAM_HPP

#include <boost/gil/concepts/concept_check.hpp>
#include <boost/gil/gray.hpp>
#include <boost/gil/histogram.hpp>
#include <boost/gil/image_view.hpp>
#include <boost/gil/image_view_factory.hpp>

#include <array>
#include <map>
#include <utility>
#include <vector>

namespace boost { namespace gil {

//////////////////////////////////////////////////////////
/// Histogram extension for STL container
//////////////////////////////////////////////////////////
/// \defgroup Histogram - STL Containers
/// \brief Collection of functions to provide histogram support in GIL using Standard
/// Template Library Containers
/// The conversion from Boost.GIL images to compatible histograms are provided. The supported
/// container types would be std::vector, std::array, std::map.
///
/// Some general constraints on STL extension:-
/// 1. Supports only 1D histogram.
/// 2. Cannot use signed images with compatible random access containers.
/// 3. Automatic resize of std::array in case of shortage of bins, to ensure
/// correctness comes before performance.
/// 4. Container key type (if exists) has to be one of std::integral types to be
/// GIL compatible.
/// 5. Container value type has to be of std::arithmetic types.
///

///
/// \ingroup Histogram - STL Containers
/// \brief Overload for std::vector of fill_histogram
///
template <typename SrcView, typename T>
void fill_histogram(SrcView const& srcview, std::vector<T>& histogram, bool accumulate = false)
{
gil_function_requires<ImageViewConcept<SrcView>>();
static_assert(std::is_arithmetic<T>::value, "Improper container type for images.");
static_assert(
std::is_unsigned<typename channel_type<SrcView>::type>::value,
"Improper container type for signed images.");

using channel_t = typename channel_type<SrcView>::type;
using pixel_t = pixel<channel_t, gray_layout_t>;

if (!accumulate)
histogram.clear();
histogram.resize(std::numeric_limits<channel_t>::max() + 1);

for_each_pixel(color_converted_view<pixel_t>(srcview), [&](pixel_t const& p) {
++histogram[static_cast<std::size_t>(p)];
});
}

/// \ingroup Histogram - STL Containers
/// \brief Overload for std::array of fill_histogram
///
template <typename SrcView, typename T, std::size_t N>
void fill_histogram(SrcView const& srcview, std::array<T, N>& histogram, bool accumulate = false)
{
gil_function_requires<ImageViewConcept<SrcView>>();
static_assert(std::is_arithmetic<T>::value && N > 0, "Improper container type for images.");
static_assert(
std::is_unsigned<typename channel_type<SrcView>::type>::value,
"Improper container type for signed images.");

using channel_t = typename channel_type<SrcView>::type;
using pixel_t = pixel<channel_t, gray_layout_t>;

const size_t pixel_max = std::numeric_limits<channel_t>::max();
const float scale = (histogram.size() - 1.0f) / pixel_max;

if (!accumulate)
std::fill(std::begin(histogram), std::end(histogram), 0);

for_each_pixel(color_converted_view<pixel_t>(srcview), [&](pixel_t const& p) {
++histogram[static_cast<std::size_t>(p * scale)];
});
}

/// \ingroup Histogram - STL Containers
/// \brief Overload for std::map of fill_histogram
///
template <typename SrcView, typename T1, typename T2>
void fill_histogram(SrcView const& srcview, std::map<T1, T2>& histogram, bool accumulate = false)
{
gil_function_requires<ImageViewConcept<SrcView>>();
static_assert(
std::is_arithmetic<T1>::value && std::is_integral<T2>::value,
"Improper container type for images.");

using channel_t = typename channel_type<SrcView>::type;
using pixel_t = pixel<channel_t, gray_layout_t>;

if (!accumulate)
histogram.clear();

for_each_pixel(color_converted_view<pixel_t>(srcview), [&](pixel_t const& p) {
++histogram[static_cast<std::size_t>(p)];
});
}

/// \ingroup Histogram - STL Containers
/// \brief Overload for std::vector of cumulative_histogram
///
template <typename T>
std::vector<T> cumulative_histogram(std::vector<T>& hist)
{
std::vector<T> cumulative_hist(hist.size());
static_assert(std::is_arithmetic<T>::value, "Improper container type for images.");
T cumulative_counter = 0;
for (std::size_t i = 0; i < hist.size(); i++)
{
cumulative_counter += hist[i];
cumulative_hist[i] = cumulative_counter;
}
return cumulative_hist;
}

/// \ingroup Histogram - STL Containers
/// \brief Overload for std::array of cumulative_histogram
///
template <typename T, std::size_t N>
std::array<T, N> cumulative_histogram(std::array<T, N>& histogram)
{
std::array<T, N> cumulative_hist;
static_assert(std::is_arithmetic<T>::value && N > 0, "Improper container type for images.");
T cumulative_counter = 0;
for (std::size_t i = 0; i < N; i++)
{
cumulative_counter += histogram[i];
cumulative_hist[i] = cumulative_counter;
}
return cumulative_hist;
}

/// \ingroup Histogram - STL Containers
/// \brief Overload for std::map of cumulative_histogram
///
template <typename T1, typename T2>
std::map<T1, T2> cumulative_histogram(std::map<T1, T2>& histogram)
{
std::map<T1, T2> cumulative_hist;
static_assert(
std::is_arithmetic<T1>::value && std::is_integral<T2>::value,
"Improper container type for images.");
T2 cumulative_counter = 0;
for (auto const& it : histogram)
{
cumulative_counter += it.second;
cumulative_hist[it.first] = cumulative_counter;
}
return cumulative_hist;
}

}} // namespace boost::gil

#endif
Loading

0 comments on commit 3e729e5

Please sign in to comment.