Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add adaptive threshold algorithm using mean method #341

Merged
merged 2 commits into from
Jul 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
126 changes: 121 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 @@ -29,11 +37,6 @@ 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 Down Expand Up @@ -83,6 +86,12 @@ enum class threshold_truncate_mode
zero ///< \todo TODO
};

enum class threshold_adaptive_method
{
mean,
gaussian
};

/// \ingroup ImageProcessing
/// \brief Applies fixed threshold to each pixel of image view.
/// Performs image binarization by thresholding channel value of each
Expand Down Expand Up @@ -334,6 +343,113 @@ 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,
typename channel_type<DstView>::type constant = 0
)
{
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)
{
std::vector<float> mean_kernel_values(kernel_size, 1.0f/kernel_size);
kernel_1d<float> kernel(mean_kernel_values.begin(), kernel_size, kernel_size/2);

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

convolve<pixel<float, typename SrcView::value_type::layout_t>>(
src_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, constant](source_channel_t px, source_channel_t threshold) -> result_channel_t
{ return px > (threshold - constant) ? max_value : 0; });
}
else
{
detail::adaptive_impl<source_channel_t, result_channel_t>(src_view, temp_conv, dst_view,
[max_value, constant](source_channel_t px, source_channel_t threshold) -> result_channel_t
{ return px > (threshold - constant) ? 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,
int constant = 0
)
{
//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, constant);
}

/// @}

}} //namespace boost::gil
Expand Down