diff --git a/example/adaptive_threshold.cpp b/example/adaptive_threshold.cpp new file mode 100644 index 0000000000..168210c10a --- /dev/null +++ b/example/adaptive_threshold.cpp @@ -0,0 +1,29 @@ +// +// Copyright 2019 Miral Shah +// +// 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 +#include + +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; +} diff --git a/example/test_adaptive.png b/example/test_adaptive.png new file mode 100644 index 0000000000..8aea767ab8 Binary files /dev/null and b/example/test_adaptive.png differ diff --git a/include/boost/gil/image_processing/threshold.hpp b/include/boost/gil/image_processing/threshold.hpp index 22d6d8c573..c527173e0a 100644 --- a/include/boost/gil/image_processing/threshold.hpp +++ b/include/boost/gil/image_processing/threshold.hpp @@ -11,7 +11,15 @@ #include #include #include +#include +#include +#include +#include + #include +#include +#include +#include namespace boost { namespace gil { @@ -30,11 +38,7 @@ void threshold_impl(SrcView const& src_view, DstView const& dst_view, Operator c { gil_function_requires>(); gil_function_requires>(); - gil_function_requires::type, - typename color_space_type::type> - >(); + static_assert(color_spaces_are_compatible < typename color_space_type::type, @@ -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. @@ -116,6 +121,8 @@ void threshold_binary //deciding output channel type and creating functor using result_channel_t = typename channel_type::type; + typedef typename channel_type::type result_channel_t; + result_channel_t max_value = std::numeric_limits::max(); threshold_binary(src_view, dst_view, threshold_value, max_value, direction); } @@ -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>(); + gil_function_requires>(); + + static_assert(color_spaces_are_compatible + < + typename color_space_type::type, + typename color_space_type::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 +void threshold_adaptive +( + SrcView const& src_view, + DstView const& dst_view, + typename channel_type::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::type source_channel_t; + typedef typename channel_type::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 temp_img(src_view.width(), src_view.height()); + image::view_t temp_view = view(temp_img); + SrcView temp_conv(temp_view); + + kernel_1d kernel(mean, kernel_size, kernel_size/2); + convolve_rows>(src_view, kernel, temp_view); + convolve_cols>(temp_view, kernel, temp_view); + + if (direction == threshold_direction::regular) + { + detail::adaptive_impl(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(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 +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::type result_channel_t; + + result_channel_t max_value = std::numeric_limits::max(); + + threshold_adaptive(src_view, dst_view, max_value, kernel_size, method, direction); +} }} //namespace boost::gil #endif //BOOST_GIL_IMAGE_PROCESSING_THRESHOLD_HPP