From ac587f9763d9b427bce7e52c0a7a51238ece9180 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Mon, 27 Jun 2022 20:49:33 +0200 Subject: [PATCH 1/2] feat: Generalized threshold algorithm for all kind of pixel types --- .../boost/gil/image_processing/threshold.hpp | 105 +++++++++--------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/include/boost/gil/image_processing/threshold.hpp b/include/boost/gil/image_processing/threshold.hpp index b3b56356fc..37963127f3 100644 --- a/include/boost/gil/image_processing/threshold.hpp +++ b/include/boost/gil/image_processing/threshold.hpp @@ -9,29 +9,27 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_THRESHOLD_HPP #define BOOST_GIL_IMAGE_PROCESSING_THRESHOLD_HPP -#include -#include -#include -#include -#include -#include -#include - #include +#include #include -#include #include +#include #include +#include +#include +#include +#include +#include +#include + namespace boost { namespace gil { namespace detail { template < - typename SourceChannelT, - typename ResultChannelT, typename SrcView, typename DstView, typename Operator @@ -59,7 +57,7 @@ void threshold_impl(SrcView const& src_view, DstView const& dst_view, Operator c } } -} //namespace boost::gil::detail +} // namespace detail /// \addtogroup ImageProcessing /// @{ @@ -114,22 +112,24 @@ void threshold_binary( threshold_direction direction = threshold_direction::regular ) { - //deciding output channel type and creating functor using source_channel_t = typename channel_type::type; using result_channel_t = typename channel_type::type; + auto const min_value = channel_traits::min_value(); + max_value = (std::min)(max_value, channel_traits::max_value()); + if (direction == threshold_direction::regular) { - detail::threshold_impl(src_view, dst_view, - [threshold_value, max_value](source_channel_t px) -> result_channel_t { - return px > threshold_value ? max_value : 0; + detail::threshold_impl(src_view, dst_view, + [&](source_channel_t px) -> result_channel_t { + return px > threshold_value ? max_value : min_value; }); } else { - detail::threshold_impl(src_view, dst_view, - [threshold_value, max_value](source_channel_t px) -> result_channel_t { - return px > threshold_value ? 0 : max_value; + detail::threshold_impl(src_view, dst_view, + [&](source_channel_t px) -> result_channel_t { + return px > threshold_value ? min_value : max_value; }); } } @@ -152,10 +152,9 @@ void threshold_binary( threshold_direction direction = threshold_direction::regular ) { - //deciding output channel type and creating functor using result_channel_t = typename channel_type::type; - result_channel_t max_value = (std::numeric_limits::max)(); + result_channel_t max_value = channel_traits::max_value(); threshold_binary(src_view, dst_view, threshold_value, max_value, direction); } @@ -179,24 +178,23 @@ void threshold_truncate( threshold_direction direction = threshold_direction::regular ) { - //deciding output channel type and creating functor using source_channel_t = typename channel_type::type; using result_channel_t = typename channel_type::type; - std::function threshold_logic; + auto const min_value = channel_traits::min_value(); if (mode == threshold_truncate_mode::threshold) { if (direction == threshold_direction::regular) { - detail::threshold_impl(src_view, dst_view, + detail::threshold_impl(src_view, dst_view, [threshold_value](source_channel_t px) -> result_channel_t { return px > threshold_value ? threshold_value : px; }); } else { - detail::threshold_impl(src_view, dst_view, + detail::threshold_impl(src_view, dst_view, [threshold_value](source_channel_t px) -> result_channel_t { return px > threshold_value ? px : threshold_value; }); @@ -206,34 +204,34 @@ void threshold_truncate( { if (direction == threshold_direction::regular) { - detail::threshold_impl(src_view, dst_view, - [threshold_value](source_channel_t px) -> result_channel_t { - return px > threshold_value ? px : 0; + detail::threshold_impl(src_view, dst_view, + [threshold_value, min_value](source_channel_t px) -> result_channel_t { + return px > threshold_value ? px : min_value; }); } else { - detail::threshold_impl(src_view, dst_view, - [threshold_value](source_channel_t px) -> result_channel_t { - return px > threshold_value ? 0 : px; + detail::threshold_impl(src_view, dst_view, + [threshold_value, min_value](source_channel_t px) -> result_channel_t { + return px > threshold_value ? min_value : px; }); } } } -namespace detail{ +namespace detail { template void otsu_impl(SrcView const& src_view, DstView const& dst_view, threshold_direction direction) { - //deciding output channel type and creating functor using source_channel_t = typename channel_type::type; + using result_channel_t = typename channel_type::type; std::array histogram{}; //initial value of min is set to maximum possible value to compare histogram data //initial value of max is set to minimum possible value to compare histogram data - auto min = (std::numeric_limits::max)(), - max = (std::numeric_limits::min)(); + auto min = channel_traits::max_value(); + auto max = channel_traits::min_value(); if (sizeof(source_channel_t) > 1 || std::is_signed::value) { @@ -324,7 +322,8 @@ void otsu_impl(SrcView const& src_view, DstView const& dst_view, threshold_direc threshold_binary(src_view, dst_view, threshold, direction); } } -} //namespace detail + +} // namespace detail template void threshold_optimal @@ -349,8 +348,6 @@ namespace detail { template < - typename SourceChannelT, - typename ResultChannelT, typename SrcView, typename DstView, typename Operator @@ -386,7 +383,8 @@ void adaptive_impl } } } -} //namespace boost::gil::detail + +} // namespace detail template void threshold_adaptive @@ -402,8 +400,11 @@ void threshold_adaptive { 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; + using source_channel_t = typename channel_type::type; + using result_channel_t = typename channel_type::type; + + auto const min_value = channel_traits::min_value(); + max_value = (std::min)(max_value, channel_traits::max_value()); image temp_img(src_view.width(), src_view.height()); typename image::view_t temp_view = view(temp_img); @@ -427,15 +428,15 @@ void threshold_adaptive if (direction == threshold_direction::regular) { - detail::adaptive_impl(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; }); + detail::adaptive_impl(src_view, temp_conv, dst_view, + [&](source_channel_t px, source_channel_t threshold) -> result_channel_t + { return px > (threshold - constant) ? max_value : min_value; }); } else { - detail::adaptive_impl(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; }); + detail::adaptive_impl(src_view, temp_conv, dst_view, + [&](source_channel_t px, source_channel_t threshold) -> result_channel_t + { return px > (threshold - constant) ? min_value : max_value; }); } } @@ -450,16 +451,14 @@ void threshold_adaptive int constant = 0 ) { - //deciding output channel type and creating functor - typedef typename channel_type::type result_channel_t; - - result_channel_t max_value = (std::numeric_limits::max)(); + using result_channel_t = typename channel_type::type; + result_channel_t max_value = channel_traits::max_value(); threshold_adaptive(src_view, dst_view, max_value, kernel_size, method, direction, constant); } /// @} -}} //namespace boost::gil +}} // namespace boost::gil -#endif //BOOST_GIL_IMAGE_PROCESSING_THRESHOLD_HPP +#endif From f37758dc43c6b4e08d6f2a1d2b8280a15477621c Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Tue, 28 Jun 2022 18:39:13 +0200 Subject: [PATCH 2/2] added tests for signed integer and floating point channel types for threshold algorithms --- .../image_processing/threshold_binary.cpp | 259 ++++++++------ .../image_processing/threshold_truncate.cpp | 321 ++++++++---------- 2 files changed, 304 insertions(+), 276 deletions(-) diff --git a/test/core/image_processing/threshold_binary.cpp b/test/core/image_processing/threshold_binary.cpp index fc39046454..42edabe4aa 100644 --- a/test/core/image_processing/threshold_binary.cpp +++ b/test/core/image_processing/threshold_binary.cpp @@ -14,124 +14,183 @@ namespace gil = boost::gil; -int height = 4; -int width = 4; +namespace { -gil::gray8_image_t original_gray(width, height), threshold_gray(width, height), -expected_gray(width, height); - -gil::rgb8_image_t original_rgb(width, height), threshold_rgb(width, height), -expected_rgb(width, height); - - -void fill_original_gray() +template +void fill_upper_and_lower_half( + View const& view, Pixel const& upper_half_pixel, Pixel const& lower_half_pixel) { - //filling original_gray view's upper half part with gray pixels of value 50 - //filling original_gray view's lower half part with gray pixels of value 150 - gil::fill_pixels(gil::subimage_view(gil::view(original_gray), 0, 0, original_gray.width(), - original_gray.height() / 2), gil::gray8_pixel_t(50)); - gil::fill_pixels(gil::subimage_view(gil::view(original_gray), 0, original_gray.height() / 2, - original_gray.width(), original_gray.height() / 2), gil::gray8_pixel_t(150)); + fill_pixels( + subimage_view(view, 0, 0, view.width(), view.height() / 2), + upper_half_pixel); + fill_pixels( + subimage_view(view, 0, view.height() / 2, view.width(), view.height() / 2), + lower_half_pixel); } -void fill_original_rgb() +template +void test_threshold( + gil::threshold_direction direction, + ChannelValue threshold_value, + ChannelValue max_value, + bool set_max_value, + Pixel const& upper_half_pixel, + Pixel const& lower_half_pixel, + Pixel const& expected_upper_half_pixel, + Pixel const& expected_lower_half_pixel) { - //filling original_rgb view's upper half part with rgb pixels of value 50, 155, 115 - //filling original_rgb view's lower half part with rgb pixels of value 203, 9, 60 - gil::fill_pixels(gil::subimage_view(gil::view(original_rgb), 0, 0, original_rgb.width(), - original_rgb.height() / 2), gil::rgb8_pixel_t(50, 155, 115)); - gil::fill_pixels(gil::subimage_view(gil::view(original_rgb), 0, original_rgb.height() / 2, - original_rgb.width(), original_rgb.height() / 2), gil::rgb8_pixel_t(203, 9, 60)); + int const height = 4; + int const width = 4; + + Image original_img(width, height); + Image threshold_img(width, height); + Image expected_img(width, height); + + auto original_view = gil::view(original_img); + auto threshold_view = gil::view(threshold_img); + auto expected_view = gil::view(expected_img); + + fill_upper_and_lower_half( + original_view, upper_half_pixel, lower_half_pixel); + fill_upper_and_lower_half( + expected_view, expected_upper_half_pixel, expected_lower_half_pixel); + + if (set_max_value) + { + threshold_binary( + original_view, threshold_view, + threshold_value, max_value, direction); + } + else + { + threshold_binary(original_view, threshold_view, threshold_value, direction); + } + + BOOST_TEST(equal_pixels(threshold_view, expected_view)); } -void binary_gray_to_gray() -{ - //expected_gray view after thresholding of the original_gray view with threshold_gray value of 100 - //filling expected_gray view's upper half part with gray pixels of value 0 - //filling expected_gray view's lower half part with gray pixels of value 255 - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, 0, original_gray.width(), - original_gray.height() / 2), gil::gray8_pixel_t(0)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, original_gray.height() / 2, - original_gray.width(), original_gray.height() / 2), gil::gray8_pixel_t(255)); - - gil::threshold_binary(gil::view(original_gray), gil::view(threshold_gray), 100); - - //comparing threshold_gray view generated by the function with the expected_gray view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_gray), gil::view(expected_gray))); -} +} // namespace -void binary_inverse_gray_to_gray() +void test_threshold_binary() { - //expected_gray view after thresholding of the original_gray view with threshold_gray value of 100 - //filling expected_gray view's upper half part with gray pixels of value 200 - //filling expected_gray view's lower half part with gray pixels of value 0 - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, 0, original_gray.width(), - original_gray.height() / 2), gil::gray8_pixel_t(200)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, original_gray.height() / 2, - original_gray.width(), original_gray.height() / 2), gil::gray8_pixel_t(0)); - - gil::threshold_binary - ( - gil::view(original_gray), - gil::view(threshold_gray), + // threshold binary should set all pixels below the threshold to channel min + // and above the threshold to the given max value or the channel max if no + // max is supplied + test_threshold( + gil::threshold_direction::regular, 100, - 200, - gil::threshold_direction::inverse - ); - - //comparing threshold_gray view generated by the function with the expected_gray view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_gray), gil::view(expected_gray))); -} - -void binary_rgb_to_rgb() -{ - //expected_rgb view after thresholding of the original_rgb view with threshold value of 100 - //filling expected_rgb view's upper half part with rgb pixels of value 0, 165, 165 - //filling expected_rgb view's lower half part with rgb pixels of value 165, 0, 0 - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, 0, original_rgb.width(), - original_rgb.height() / 2), gil::rgb8_pixel_t(0, 165, 165)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, original_rgb.height() / 2, - original_rgb.width(), original_rgb.height() / 2), gil::rgb8_pixel_t(165, 0, 0)); - - gil::threshold_binary(gil::view(original_rgb), gil::view(threshold_rgb), 100, 165); - - //comparing threshold_rgb view generated by the function with the expected_rgb view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_rgb), gil::view(expected_rgb))); + 150, true, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{170}, + gil::gray8_pixel_t{0}, gil::gray8_pixel_t{150}); + test_threshold( + gil::threshold_direction::regular, + 100, + 150, false, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{170}, + gil::gray8_pixel_t{0}, gil::gray8_pixel_t{255}); + + test_threshold( + gil::threshold_direction::regular, + 50, + 100, true, + gil::gray8s_pixel_t{50}, gil::gray8s_pixel_t{110}, + gil::gray8s_pixel_t{-128}, gil::gray8s_pixel_t{100}); + test_threshold( + gil::threshold_direction::regular, + 50, + 100, false, + gil::gray8s_pixel_t{50}, gil::gray8s_pixel_t{110}, + gil::gray8s_pixel_t{-128}, gil::gray8s_pixel_t{127}); + + test_threshold( + gil::threshold_direction::regular, + 0.5f, + 0.6f, true, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.7f}, + gil::gray32f_pixel_t{0.0f}, gil::gray32f_pixel_t{0.6f}); + test_threshold( + gil::threshold_direction::regular, + 0.5f, + 0.6f, false, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.7f}, + gil::gray32f_pixel_t{0.0f}, gil::gray32f_pixel_t{1.0f}); + + test_threshold( + gil::threshold_direction::regular, + 100, + 165, true, + gil::rgb8_pixel_t{50, 155, 115}, gil::rgb8_pixel_t{203, 9, 60}, + gil::rgb8_pixel_t{0, 165, 165}, gil::rgb8_pixel_t{165, 0, 0}); + test_threshold( + gil::threshold_direction::regular, + 100, + 165, false, + gil::rgb8_pixel_t{50, 155, 115}, gil::rgb8_pixel_t{203, 9, 60}, + gil::rgb8_pixel_t{0, 255, 255}, gil::rgb8_pixel_t{255, 0, 0}); } -void binary_inverse_rgb_to_rgb() +void test_threshold_binary_inverse() { - //expected_rgb view after thresholding of the original_rgb view with threshold value of 100 - //filling expected_rgb view's upper half part with rgb pixels of value 90, 0, 0 - //filling expected_rgb view's lower half part with rgb pixels of value 0, 90, 90 - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, 0, original_rgb.width(), - original_rgb.height() / 2), gil::rgb8_pixel_t(90, 0, 0)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, original_rgb.height() / 2, - original_rgb.width(), original_rgb.height() / 2), gil::rgb8_pixel_t(0, 90, 90)); - - gil::threshold_binary - ( - gil::view(original_rgb), - gil::view(threshold_rgb), + // inverse threshold binary should set all pixels above the threshold to channel min + // and below the threshold to the given max value or the channel max if no + // max is supplied + test_threshold( + gil::threshold_direction::inverse, 100, - 90, - gil::threshold_direction::inverse - ); - - //comparing threshold_rgb view generated by the function with the expected_rgb view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_rgb), gil::view(expected_rgb))); + 150, true, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{170}, + gil::gray8_pixel_t{150}, gil::gray8_pixel_t{0}); + test_threshold( + gil::threshold_direction::inverse, + 100, + 150, false, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{170}, + gil::gray8_pixel_t{255}, gil::gray8_pixel_t{0}); + + test_threshold( + gil::threshold_direction::inverse, + 50, + 100, true, + gil::gray8s_pixel_t{50}, gil::gray8s_pixel_t{110}, + gil::gray8s_pixel_t{100}, gil::gray8s_pixel_t{-128}); + test_threshold( + gil::threshold_direction::inverse, + 50, + 100, false, + gil::gray8s_pixel_t{50}, gil::gray8s_pixel_t{110}, + gil::gray8s_pixel_t{127}, gil::gray8s_pixel_t{-128}); + + test_threshold( + gil::threshold_direction::inverse, + 0.5f, + 0.6f, true, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.7f}, + gil::gray32f_pixel_t{0.6f}, gil::gray32f_pixel_t{0.0f}); + test_threshold( + gil::threshold_direction::inverse, + 0.5f, + 0.6f, false, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.7f}, + gil::gray32f_pixel_t{1.0f}, gil::gray32f_pixel_t{0.0f}); + + test_threshold( + gil::threshold_direction::inverse, + 100, + 165, true, + gil::rgb8_pixel_t{50, 155, 115}, gil::rgb8_pixel_t{203, 9, 60}, + gil::rgb8_pixel_t{165, 0, 0}, gil::rgb8_pixel_t{0, 165, 165}); + test_threshold( + gil::threshold_direction::inverse, + 100, + 165, false, + gil::rgb8_pixel_t{50, 155, 115}, gil::rgb8_pixel_t{203, 9, 60}, + gil::rgb8_pixel_t{255, 0, 0}, gil::rgb8_pixel_t{0, 255, 255}); } - int main() { - fill_original_gray(); - fill_original_rgb(); - - binary_gray_to_gray(); - binary_inverse_gray_to_gray(); - binary_rgb_to_rgb(); - binary_inverse_rgb_to_rgb(); + test_threshold_binary(); + test_threshold_binary_inverse(); return boost::report_errors(); } diff --git a/test/core/image_processing/threshold_truncate.cpp b/test/core/image_processing/threshold_truncate.cpp index 18266dec67..8bad3b881e 100644 --- a/test/core/image_processing/threshold_truncate.cpp +++ b/test/core/image_processing/threshold_truncate.cpp @@ -14,220 +14,189 @@ namespace gil = boost::gil; -int height = 4; -int width = 4; +namespace { -gil::gray8_image_t original_gray(width, height), threshold_gray(width, height), -expected_gray(width, height); - -gil::rgb8_image_t original_rgb(width, height), threshold_rgb(width, height), -expected_rgb(width, height); - -void fill_original_gray() +template +void fill_upper_and_lower_half( + View const& view, Pixel const& upper_half_pixel, Pixel const& lower_half_pixel) { - //filling original view's upper half part with gray pixels of value 50 - //filling original view's lower half part with gray pixels of value 150 - gil::fill_pixels(gil::subimage_view(gil::view(original_gray), 0, 0, original_gray.width(), - original_gray.height() / 2), gil::gray8_pixel_t(50)); - gil::fill_pixels(gil::subimage_view(gil::view(original_gray), 0, original_gray.height() / 2, - original_gray.width(), original_gray.height() / 2), gil::gray8_pixel_t(150)); + fill_pixels( + subimage_view(view, 0, 0, view.width(), view.height() / 2), + upper_half_pixel); + fill_pixels( + subimage_view(view, 0, view.height() / 2, view.width(), view.height() / 2), + lower_half_pixel); } -void fill_original_rgb() +template +void test_threshold( + gil::threshold_truncate_mode mode, + gil::threshold_direction direction, + ChannelValue threshold_value, + Pixel const& upper_half_pixel, + Pixel const& lower_half_pixel, + Pixel const& expected_upper_half_pixel, + Pixel const& expected_lower_half_pixel) { - //filling original_rgb view's upper half part with rgb pixels of value 50, 85, 135 - //filling original_rgb view's lower half part with rgb pixels of value 150, 205, 106 - gil::fill_pixels(gil::subimage_view(gil::view(original_rgb), 0, 0, original_rgb.width(), - original_rgb.height() / 2), gil::rgb8_pixel_t(50, 85, 135)); - gil::fill_pixels(gil::subimage_view(gil::view(original_rgb), 0, original_rgb.height() / 2, - original_rgb.width(), original_rgb.height() / 2), gil::rgb8_pixel_t(150, 205, 106)); -} + int const height = 4; + int const width = 4; -void threshold_gray_to_gray() -{ - //expected view after thresholding of the original view with threshold value of 100 - //filling expected view's upper half part with gray pixels of value 50 - //filling expected view's lower half part with gray pixels of value 100 - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, 0, original_gray.width(), - original_gray.height() / 2), gil::gray8_pixel_t(50)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, original_gray.height() / 2, - original_gray.width(), original_gray.height() / 2), gil::gray8_pixel_t(100)); - - gil::threshold_truncate(gil::view(original_gray), gil::view(threshold_gray), 100); - - //comparing threshold view generated by the function with the expected view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_gray), gil::view(expected_gray))); -} + Image original_img(width, height); + Image threshold_img(width, height); + Image expected_img(width, height); -void threshold_inverse_gray_to_gray() -{ - //expected view after thresholding of the original view with threshold value of 100 - //filling expected view's upper half part with gray pixels of value 100 - //filling expected view's lower half part with gray pixels of value 150 - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, 0, original_gray.width(), - original_gray.height() / 2), gil::gray8_pixel_t(100)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, original_gray.height() / 2, - original_gray.width(), original_gray.height() / 2), gil::gray8_pixel_t(150)); - - gil::threshold_truncate - ( - gil::view(original_gray), - gil::view(threshold_gray), - 100, - gil::threshold_truncate_mode::threshold, - gil::threshold_direction::inverse - ); + auto original_view = gil::view(original_img); + auto threshold_view = gil::view(threshold_img); + auto expected_view = gil::view(expected_img); - //comparing threshold view generated by the function with the expected view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_gray), gil::view(expected_gray))); -} + fill_upper_and_lower_half( + original_view, upper_half_pixel, lower_half_pixel); + fill_upper_and_lower_half( + expected_view, expected_upper_half_pixel, expected_lower_half_pixel); -void zero_gray_to_gray() -{ - //expected view after thresholding of the original view with threshold value of 100 - //filling expected view's upper half part with gray pixels of value 0 - //filling expected view's lower half part with gray pixels of value 150 - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, 0, original_gray.width(), - original_gray.height() / 2), gil::gray8_pixel_t(0)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, original_gray.height() / 2, - original_gray.width(), original_gray.height() / 2), gil::gray8_pixel_t(150)); - - gil::threshold_truncate - ( - gil::view(original_gray), - gil::view(threshold_gray), - 100, - gil::threshold_truncate_mode::zero, - gil::threshold_direction::regular - ); + threshold_truncate(original_view, threshold_view, threshold_value, mode, direction); - //comparing threshold view generated by the function with the expected view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_gray), gil::view(expected_gray))); + BOOST_TEST(equal_pixels(threshold_view, expected_view)); } -void zero_inverse_gray_to_gray() +} // namespace + +void test_threshold_truncate_regular() { - //expected view after thresholding of the original view with threshold value of 100 - //filling expected view's upper half part with gray pixels of value 50 - //filling expected view's lower half part with gray pixels of value 0 - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, 0, original_gray.width(), - original_gray.height() / 2), gil::gray8_pixel_t(50)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_gray), 0, original_gray.height() / 2, - original_gray.width(), original_gray.height() / 2), gil::gray8_pixel_t(0)); - - gil::threshold_truncate - ( - gil::view(original_gray), - gil::view(threshold_gray), + // truncation mode threshold and direction regular should clip all pixels + // above the threshold to the threshold value + test_threshold( + gil::threshold_truncate_mode::threshold, + gil::threshold_direction::regular, 100, - gil::threshold_truncate_mode::zero, - gil::threshold_direction::inverse - ); + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{150}, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{100}); - //comparing threshold view generated by the function with the expected view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_gray), gil::view(expected_gray))); -} + test_threshold( + gil::threshold_truncate_mode::threshold, + gil::threshold_direction::regular, + 100, + gil::gray8s_pixel_t{-50}, gil::gray8s_pixel_t{120}, + gil::gray8s_pixel_t{-50}, gil::gray8s_pixel_t{100}); -void threshold_rgb_to_rgb() -{ - //expected view after thresholding of the original view with threshold value of 100 - //filling expected_rgb view's upper half part with rgb pixels of value 50 - //filling expected_rgb view's lower half part with rgb pixels of value 100 - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, 0, original_rgb.width(), - original_rgb.height() / 2), gil::rgb8_pixel_t(50, 85, 100)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, original_rgb.height() / 2, - original_rgb.width(), original_rgb.height() / 2), gil::rgb8_pixel_t(100, 100, 100)); - - gil::threshold_truncate(gil::view(original_rgb), gil::view(threshold_rgb), 100); - - //comparing threshold_rgb view generated by the function with the expected_rgb view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_rgb), gil::view(expected_rgb))); + test_threshold( + gil::threshold_truncate_mode::threshold, + gil::threshold_direction::regular, + 0.5f, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.7f}, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.5f}); + + test_threshold( + gil::threshold_truncate_mode::threshold, + gil::threshold_direction::regular, + 100, + gil::rgb8_pixel_t{50, 85, 135}, gil::rgb8_pixel_t{150, 205, 106}, + gil::rgb8_pixel_t{50, 85, 100}, gil::rgb8_pixel_t{100, 100, 100}); } -void threshold_inverse_rgb_to_rgb() +void test_threshold_truncate_inverse() { - //expected view after thresholding of the original view with threshold value of 100 - //filling expected_rgb view's upper half part with rgb pixels of value 103, 59, 246 - //filling expected_rgb view's lower half part with rgb pixels of value 150 - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, 0, original_rgb.width(), - original_rgb.height() / 2), gil::rgb8_pixel_t(100, 100, 135)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, original_rgb.height() / 2, - original_rgb.width(), original_rgb.height() / 2), gil::rgb8_pixel_t(150, 205, 106)); - - gil::threshold_truncate - ( - gil::view(original_rgb), - gil::view(threshold_rgb), + // truncation mode threshold and direction inverse should clip all pixels + // below the threshold to the threshold value + test_threshold( + gil::threshold_truncate_mode::threshold, + gil::threshold_direction::inverse, + 100, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{150}, + gil::gray8_pixel_t{100}, gil::gray8_pixel_t{150}); + + test_threshold( + gil::threshold_truncate_mode::threshold, + gil::threshold_direction::inverse, 100, + gil::gray8s_pixel_t{-50}, gil::gray8s_pixel_t{120}, + gil::gray8s_pixel_t{100}, gil::gray8s_pixel_t{120}); + + test_threshold( gil::threshold_truncate_mode::threshold, - gil::threshold_direction::inverse - ); + gil::threshold_direction::inverse, + 0.5f, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.7f}, + gil::gray32f_pixel_t{0.5f}, gil::gray32f_pixel_t{0.7f}); - //comparing threshold_rgb view generated by the function with the expected_rgb view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_rgb), gil::view(expected_rgb))); + test_threshold( + gil::threshold_truncate_mode::threshold, + gil::threshold_direction::inverse, + 100, + gil::rgb8_pixel_t{50, 85, 135}, gil::rgb8_pixel_t{150, 205, 106}, + gil::rgb8_pixel_t{100, 100, 135}, gil::rgb8_pixel_t{150, 205, 106}); } -void zero_rgb_to_rgb() +void test_threshold_zero() { - //expected view after thresholding of the original view with threshold value of 100 - //filling expected_rgb view's upper half part with rgb pixels of value 0 - //filling expected_rgb view's lower half part with rgb pixels of value 150 - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, 0, original_rgb.width(), - original_rgb.height() / 2), gil::rgb8_pixel_t(0, 0, 135)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, original_rgb.height() / 2, - original_rgb.width(), original_rgb.height() / 2), gil::rgb8_pixel_t(150, 205, 106)); - - gil::threshold_truncate - ( - gil::view(original_rgb), - gil::view(threshold_rgb), + // truncation mode zero and direction regular should zero all pixels below the threshold + test_threshold( + gil::threshold_truncate_mode::zero, + gil::threshold_direction::regular, + 100, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{150}, + gil::gray8_pixel_t{0}, gil::gray8_pixel_t{150}); + + test_threshold( + gil::threshold_truncate_mode::zero, + gil::threshold_direction::regular, 100, + gil::gray8s_pixel_t{-50}, gil::gray8s_pixel_t{120}, + gil::gray8s_pixel_t{-128}, gil::gray8s_pixel_t{120}); + + test_threshold( gil::threshold_truncate_mode::zero, - gil::threshold_direction::regular - ); + gil::threshold_direction::regular, + 0.5f, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.7f}, + gil::gray32f_pixel_t{0.0f}, gil::gray32f_pixel_t{0.7f}); - //comparing threshold_rgb view generated by the function with the expected_rgb view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_rgb), gil::view(expected_rgb))); + test_threshold( + gil::threshold_truncate_mode::zero, + gil::threshold_direction::regular, + 100, + gil::rgb8_pixel_t{50, 85, 135}, gil::rgb8_pixel_t{150, 205, 106}, + gil::rgb8_pixel_t{0, 0, 135}, gil::rgb8_pixel_t{150, 205, 106}); } -void zero_inverse_rgb_to_rgb() +void test_threshold_zero_inverse() { - //expected view after thresholding of the original view with threshold value of 100 - //filling expected_rgb view's upper half part with rgb pixels of value 50 - //filling expected_rgb view's lower half part with rgb pixels of value 0 - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, 0, original_rgb.width(), - original_rgb.height() / 2), gil::rgb8_pixel_t(50, 85, 0)); - gil::fill_pixels(gil::subimage_view(gil::view(expected_rgb), 0, original_rgb.height() / 2, - original_rgb.width(), original_rgb.height() / 2), gil::rgb8_pixel_t(0, 0, 0)); - - gil::threshold_truncate - ( - gil::view(original_rgb), - gil::view(threshold_rgb), + // truncation mode zero and direction inverse should zero all pixels above the threshold + test_threshold( + gil::threshold_truncate_mode::zero, + gil::threshold_direction::inverse, 100, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{150}, + gil::gray8_pixel_t{50}, gil::gray8_pixel_t{0}); + + test_threshold( gil::threshold_truncate_mode::zero, - gil::threshold_direction::inverse - ); + gil::threshold_direction::inverse, + 100, + gil::gray8s_pixel_t{-50}, gil::gray8s_pixel_t{120}, + gil::gray8s_pixel_t{-50}, gil::gray8s_pixel_t{-128}); - //comparing threshold_rgb view generated by the function with the expected_rgb view - BOOST_TEST(gil::equal_pixels(gil::view(threshold_rgb), gil::view(expected_rgb))); -} + test_threshold( + gil::threshold_truncate_mode::zero, + gil::threshold_direction::inverse, + 0.5f, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.7f}, + gil::gray32f_pixel_t{0.3f}, gil::gray32f_pixel_t{0.0f}); + test_threshold( + gil::threshold_truncate_mode::zero, + gil::threshold_direction::inverse, + 100, + gil::rgb8_pixel_t{50, 85, 135}, gil::rgb8_pixel_t{150, 205, 106}, + gil::rgb8_pixel_t{50, 85, 0}, gil::rgb8_pixel_t{0, 0, 0}); +} int main() { - fill_original_gray(); - fill_original_rgb(); - - threshold_gray_to_gray(); - threshold_inverse_gray_to_gray(); - zero_gray_to_gray(); - zero_inverse_gray_to_gray(); - - threshold_rgb_to_rgb(); - threshold_inverse_rgb_to_rgb(); - zero_rgb_to_rgb(); - zero_inverse_rgb_to_rgb(); + test_threshold_truncate_regular(); + test_threshold_truncate_inverse(); + test_threshold_zero(); + test_threshold_zero_inverse(); return boost::report_errors(); }