Skip to content

Commit

Permalink
Mean adaptive threshold implemeted with example
Browse files Browse the repository at this point in the history
closes #315
  • Loading branch information
miralshah365 committed Jul 22, 2019
1 parent d86e4a4 commit 7c0f386
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 5 deletions.
29 changes: 29 additions & 0 deletions example/adaptive_threshold.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Copyright 2019 Miral Shah <miralshah2211@gmail.com>
//
// Use, modification and distribution are subject to 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/extension/io/png.hpp>
#include <boost/gil/image_processing/threshold.hpp>

using namespace boost::gil;

int main()
{
gray8_image_t img;
read_image("test_adaptive.png", img, png_tag{});
gray8_image_t img_out(img.dimensions());

// performing binary threshold on each channel of the image
// if the pixel value is more than 150 than it will be set to 255 else to 0
boost::gil::threshold_adaptive(const_view(img), view(img_out), 11);
write_view("out-threshold-adaptive.png", view(img_out), png_tag{});

// if the pixel value is more than 150 than it will be set to 150 else no change
boost::gil::threshold_adaptive(const_view(img), view(img_out), 11, threshold_adaptive_method::mean, threshold_direction::inverse);
write_view("out-threshold-adaptive-inv.png", view(img_out), png_tag{});

return 0;
}
Binary file added example/test_adaptive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
121 changes: 116 additions & 5 deletions include/boost/gil/image_processing/threshold.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@
#include <limits>
#include <array>
#include <type_traits>
#include <cstddef>
#include <algorithm>
#include <vector>
#include <cmath>

#include <boost/gil/image.hpp>
#include <boost/gil/extension/numeric/kernel.hpp>
#include <boost/gil/extension/numeric/convolve.hpp>
#include <boost/assert.hpp>

namespace boost { namespace gil {

Expand All @@ -30,11 +38,7 @@ void threshold_impl(SrcView const& src_view, DstView const& dst_view, Operator c
{
gil_function_requires<ImageViewConcept<SrcView>>();
gil_function_requires<MutableImageViewConcept<DstView>>();
gil_function_requires<ColorSpacesCompatibleConcept
<
typename color_space_type<SrcView>::type,
typename color_space_type<DstView>::type>
>();

static_assert(color_spaces_are_compatible
<
typename color_space_type<SrcView>::type,
Expand All @@ -59,6 +63,7 @@ void threshold_impl(SrcView const& src_view, DstView const& dst_view, Operator c
enum class threshold_direction { regular, inverse };
enum class threshold_optimal_value { otsu, triangle };
enum class threshold_truncate_mode { threshold, zero };
enum class threshold_adaptive_method { mean, gaussian };

/*!
Takes an image view and performes binary thresholding operation on each chennel.
Expand Down Expand Up @@ -116,6 +121,8 @@ void threshold_binary
//deciding output channel type and creating functor
using result_channel_t = typename channel_type<DstView>::type;

typedef typename channel_type<DstView>::type result_channel_t;

result_channel_t max_value = std::numeric_limits<result_channel_t>::max();
threshold_binary(src_view, dst_view, threshold_value, max_value, direction);
}
Expand Down Expand Up @@ -308,6 +315,110 @@ void threshold_optimal
}
}

namespace detail {

template
<
typename SourceChannelT,
typename ResultChannelT,
typename SrcView,
typename DstView,
typename Operator
>
void adaptive_impl
(
SrcView const& src_view,
SrcView const& convolved_view,
DstView const& dst_view,
Operator const& threshold_op
)
{
//template argument validation
gil_function_requires<ImageViewConcept<SrcView>>();
gil_function_requires<MutableImageViewConcept<DstView>>();

static_assert(color_spaces_are_compatible
<
typename color_space_type<SrcView>::type,
typename color_space_type<DstView>::type
>::value, "Source and destination views must have pixels with the same color space");

//iterate over the image chaecking each pixel value for the threshold
for (std::ptrdiff_t y = 0; y < src_view.height(); y++)
{
typename SrcView::x_iterator src_it = src_view.row_begin(y);
typename SrcView::x_iterator convolved_it = convolved_view.row_begin(y);
typename DstView::x_iterator dst_it = dst_view.row_begin(y);

for (std::ptrdiff_t x = 0; x < src_view.width(); x++)
{
static_transform(src_it[x], convolved_it[x], dst_it[x], threshold_op);
}
}
}
} //namespace boost::gil::detail

template <typename SrcView, typename DstView>
void threshold_adaptive
(
SrcView const& src_view,
DstView const& dst_view,
typename channel_type<DstView>::type max_value,
std::size_t kernel_size,
threshold_adaptive_method method = threshold_adaptive_method::mean,
threshold_direction direction = threshold_direction::regular
)
{
BOOST_ASSERT_MSG((kernel_size % 2 != 0), "Kernel size must be an odd number");

typedef typename channel_type<SrcView>::type source_channel_t;
typedef typename channel_type<DstView>::type result_channel_t;

if (method == threshold_adaptive_method::mean)
{
float *mean = new float[kernel_size];
std::fill_n(mean, kernel_size, 1.0f/kernel_size);

image<typename SrcView::value_type> temp_img(src_view.width(), src_view.height());
image<typename SrcView::value_type>::view_t temp_view = view(temp_img);
SrcView temp_conv(temp_view);

kernel_1d<float> kernel(mean, kernel_size, kernel_size/2);
convolve_rows<pixel<float, SrcView::value_type::layout_t>>(src_view, kernel, temp_view);
convolve_cols<pixel<float, SrcView::value_type::layout_t>>(temp_view, kernel, temp_view);

if (direction == threshold_direction::regular)
{
detail::adaptive_impl<source_channel_t, result_channel_t>(src_view, temp_conv, dst_view,
[max_value](source_channel_t px1, source_channel_t px2) -> result_channel_t
{ return px1 >= px2 ? max_value : 0; });
}
else
{
detail::adaptive_impl<source_channel_t, result_channel_t>(src_view, temp_conv, dst_view,
[max_value](source_channel_t px1, source_channel_t px2) -> result_channel_t
{ return px1 >= px2 ? 0 : max_value; });
}
}
}

template <typename SrcView, typename DstView>
void threshold_adaptive
(
SrcView const& src_view,
DstView const& dst_view,
std::size_t kernel_size,
threshold_adaptive_method method = threshold_adaptive_method::mean,
threshold_direction direction = threshold_direction::regular
)
{
//deciding output channel type and creating functor
typedef typename channel_type<DstView>::type result_channel_t;

result_channel_t max_value = std::numeric_limits<result_channel_t>::max();

threshold_adaptive(src_view, dst_view, max_value, kernel_size, method, direction);
}
}} //namespace boost::gil

#endif //BOOST_GIL_IMAGE_PROCESSING_THRESHOLD_HPP

0 comments on commit 7c0f386

Please sign in to comment.