diff --git a/example/euclidean_distance_transform.cpp b/example/euclidean_distance_transform.cpp new file mode 100644 index 0000000000..50cac6aab0 --- /dev/null +++ b/example/euclidean_distance_transform.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2021 Harshit Pant +// +// 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 +#include +#include + +#include + +namespace gil = boost::gil; + +/// calculates distance transform based on euclidean distance measurement formulae. + +int main(int argc, char* argv[]) +{ + if (argc != 6) + { + std::cerr << "usage: " << argv[0] + << " " + " "; + return -1; + } + + std::string dist_from = argv[3]; + std::string dist_type = argv[4]; + std::string mask_size = argv[5]; + + gil::gray8_image_t input; + gil::read_image(argv[1], input, gil::png_tag{}); + gil::gray8_image_t output(input.dimensions()); + + if (dist_from == "on_pixels" && dist_type == "euclidean_approximation" && mask_size == "three") + { + distance_transform( + view(input), + view(output), + gil::distance_from::on_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::three); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if ( + dist_from == "off_pixels" && dist_type == "euclidean_approximation" && mask_size == "three") + { + distance_transform( + view(input), + view(output), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::three); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if ( + dist_from == "on_pixels" && dist_type == "euclidean_approximation" && mask_size == "five") + { + distance_transform( + view(input), + view(output), + gil::distance_from::on_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::five); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if ( + dist_from == "off_pixels" && dist_type == "euclidean_approximation" && mask_size == "five") + { + distance_transform( + view(input), + view(output), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::five); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if ( + dist_from == "on_pixels" && dist_type == "precise_euclidean" + && mask_size == "not_applicable") + { + distance_transform( + view(input), + view(output), + gil::distance_from::on_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if ( + dist_from == "off_pixels" && dist_type == "precise_euclidean" + && mask_size == "not_applicable") + { + distance_transform( + view(input), + view(output), + gil::distance_from::off_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + else + { + std::cerr << "This program fails to work with the set of arguments used. Please try again."; + } + + return 0; +} diff --git a/include/boost/gil/image_processing/distance_transform.hpp b/include/boost/gil/image_processing/distance_transform.hpp new file mode 100644 index 0000000000..96d22e3929 --- /dev/null +++ b/include/boost/gil/image_processing/distance_transform.hpp @@ -0,0 +1,572 @@ +// +// Copyright 2021 Harshit Pant +// +// 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) +// +#ifndef BOOST_GIL_IMAGE_PROCESSING_DISTANCE_TRANSFORM_HPP +#define BOOST_GIL_IMAGE_PROCESSING_DISTANCE_TRANSFORM_HPP + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace boost { namespace gil { + +namespace distance_type { +struct euclidean_approximation_t +{ +}; +struct manhattan_t +{ +}; +struct chessboard_t +{ +}; +struct precise_euclidean_t +{ +}; + +static euclidean_approximation_t euclidean_approximation; +static manhattan_t manhattan; +static chessboard_t chessboard; +static precise_euclidean_t precise_euclidean; +} // namespace distance_type + +namespace mask_size { +struct three_t +{ +}; +struct five_t +{ +}; +struct not_applicable_t +{ +}; + +static three_t three; +static five_t five; +static not_applicable_t not_applicable; +} // namespace mask_size + +enum class distance_from +{ + off_pixels, + on_pixels +}; + +namespace detail { + +/// \breif Checks if mask_size used exists. +template +struct dt_check_mask_size +{ + static constexpr bool value = std::is_same::value + || std::is_same::value + || std::is_same::value; +}; + +/// \breif Checks if distance_type used exists. +template +struct dt_check_distance_type +{ + static constexpr bool value + = std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value; +}; + +/// \breif Checks compatiblity of distance_type and mask_size used together. +template +struct dt_parameters_are_compatible : std::true_type +{ +}; + +template +struct dt_parameters_are_compatible : std::false_type +{ +}; + +template +struct dt_parameters_are_compatible : std::false_type +{ +}; + +template <> +struct dt_parameters_are_compatible + : std::true_type +{ +}; + +/// \breif Value used as infinite distance for distance_transform. +float constexpr dt_infinite = 1000000000; + +/// \breif Calculates distance transfrom with mask size three. +/// Optimal local distances a, b for euclidean approximation from, +/// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf +/// Algorithm from - Principles of Digital Image Processing: Core Algorithms (Section 11.2.2). +template +void distance_transform_mask_size_three_impl( + SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from, + DistanceType /*Passes type*/) +{ + int const padding = 1; + gray32f_image_t intermediate_image( + src_view.width() + 2 * padding, src_view.height() + 2 * padding); + gray32f_view_t intermediate_image_view = view(intermediate_image); + + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + { + intermediate_image_view(x, 0)[0] = dt_infinite; + intermediate_image_view(x, intermediate_image_view.height() - 1)[0] = dt_infinite; + } + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + { + intermediate_image_view(0, y)[0] = dt_infinite; + intermediate_image_view(intermediate_image_view.width() - 1, y)[0] = dt_infinite; + } + + float constexpr src_channel_min + = (std::numeric_limits::type>::min)(); + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if (dist_from == distance_from::off_pixels) + { + if (src_view(x, y)[0] == src_channel_min) + intermediate_image_view(x + padding, y + padding)[0] = 0; + else + intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; + } + else + { + if (src_view(x, y)[0] != src_channel_min) + intermediate_image_view(x + padding, y + padding)[0] = 0; + else + intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; + } + } + } + + float a, b; // a and b hold elements of the distance mask (see reference). + + if (std::is_same::value) + { + a = 0.95509f; + b = 1.36930f; + } + else if (std::is_same::value) + { + a = 1; + b = 2; + } + else + { + a = 1; + b = 1; + } + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if (intermediate_image_view(x + padding, y + padding)[0] > 0) + { + float const d1 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; + float const d2 = b + intermediate_image_view(x - 1 + padding, y - 1 + padding)[0]; + float const d3 = a + intermediate_image_view(x + padding, y - 1 + padding)[0]; + float const d4 = b + intermediate_image_view(x + 1 + padding, y - 1 + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] = std::min({d1, d2, d3, d4}); + } + } + } + + for (std::ptrdiff_t y = src_view.height() - 1; y >= 0; --y) + { + for (std::ptrdiff_t x = src_view.width() - 1; x >= 0; --x) + { + if (intermediate_image_view(x + padding, y + padding)[0] > 0) + { + float const d1 = a + intermediate_image_view(x + 1 + padding, y + padding)[0]; + float const d2 = b + intermediate_image_view(x + 1 + padding, y + 1 + padding)[0]; + float const d3 = a + intermediate_image_view(x + padding, y + 1 + padding)[0]; + float const d4 = b + intermediate_image_view(x - 1 + padding, y + 1 + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] = std::min( + {d1, + d2, + d3, + d4, + static_cast(intermediate_image_view(x + padding, y + padding)[0])}); + + float const distance_transform + = intermediate_image_view(x + padding, y + padding)[0]; + + float constexpr dst_channel_max + = (is_same::value) + ? dt_infinite + : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min + = (is_same::value) + ? 0.f + : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] + = static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max + : (distance_transform < dst_channel_min) ? dst_channel_min + : distance_transform); + } + } + } +} + +/// \breif Calculates distance transfrom with mask size five. +/// Optimal local distances a, b, c for euclidean approximation from, +/// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf +/// Algorithm from - Principles of Digital Image Processing: Core Algorithms (Section 11.2.2). +template +void distance_transform_mask_size_five_impl( + SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from, + DistanceType /*Passes type*/) +{ + int const padding = 2; + gray32f_image_t intermediate_image( + src_view.width() + 2 * padding, src_view.height() + 2 * padding); + gray32f_view_t intermediate_image_view = view(intermediate_image); + + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + { + intermediate_image_view(x, 0)[0] = dt_infinite; + intermediate_image_view(x, 1)[0] = dt_infinite; + intermediate_image_view(x, intermediate_image_view.height() - 1)[0] = dt_infinite; + intermediate_image_view(x, intermediate_image_view.height() - 2)[0] = dt_infinite; + } + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + { + intermediate_image_view(0, y)[0] = dt_infinite; + intermediate_image_view(1, y)[0] = dt_infinite; + intermediate_image_view(intermediate_image_view.width() - 1, y)[0] = dt_infinite; + intermediate_image_view(intermediate_image_view.width() - 2, y)[0] = dt_infinite; + } + + float constexpr src_channel_min + = (std::numeric_limits::type>::min)(); + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if (dist_from == distance_from::off_pixels) + { + if (src_view(x, y)[0] == src_channel_min) + intermediate_image_view(x + padding, y + padding)[0] = 0; + else + intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; + } + else + { + if (src_view(x, y)[0] != src_channel_min) + intermediate_image_view(x + padding, y + padding)[0] = 0; + else + intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; + } + } + } + + float a, b, c; // a, b and c hold elements of the distance mask (see reference). + + if (std::is_same::value) + { + a = 1; + b = 1.4f; + c = 2.19691f; + } + else if (std::is_same::value) + { + a = 1; + b = 2; + c = 3; + } + else + { + a = 1; + b = 1; + c = 2; + } + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if (intermediate_image_view(x + padding, y + padding)[0] > 0) + { + float const d1 = c + intermediate_image_view(x - 1 + padding, y - 2 + padding)[0]; + float const d2 = c + intermediate_image_view(x + 1 + padding, y - 2 + padding)[0]; + float const d3 = c + intermediate_image_view(x - 2 + padding, y - 1 + padding)[0]; + float const d4 = b + intermediate_image_view(x - 1 + padding, y - 1 + padding)[0]; + float const d5 = a + intermediate_image_view(x + padding, y - 1 + padding)[0]; + float const d6 = b + intermediate_image_view(x + 1 + padding, y - 1 + padding)[0]; + float const d7 = c + intermediate_image_view(x + 2 + padding, y - 1 + padding)[0]; + float const d8 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] + = std::min({d1, d2, d3, d4, d5, d6, d7, d8}); + } + } + } + + for (std::ptrdiff_t y = src_view.height() - 1; y >= 0; --y) + { + for (std::ptrdiff_t x = src_view.width() - 1; x >= 0; --x) + { + if (intermediate_image_view(x + padding, y + padding)[0] > 0) + { + float const d1 = c + intermediate_image_view(x + 1 + padding, y + 2 + padding)[0]; + float const d2 = c + intermediate_image_view(x - 1 + padding, y + 2 + padding)[0]; + float const d3 = c + intermediate_image_view(x + 2 + padding, y + 1 + padding)[0]; + float const d4 = b + intermediate_image_view(x + 1 + padding, y + 1 + padding)[0]; + float const d5 = a + intermediate_image_view(x + padding, y + 1 + padding)[0]; + float const d6 = b + intermediate_image_view(x - 1 + padding, y + 1 + padding)[0]; + float const d7 = c + intermediate_image_view(x - 2 + padding, y + 1 + padding)[0]; + float const d8 = a + intermediate_image_view(x + 1 + padding, y + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] = std::min( + {d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + static_cast(intermediate_image_view(x + padding, y + padding)[0])}); + + float const distance_transform + = intermediate_image_view(x + padding, y + padding)[0]; + + float constexpr dst_channel_max + = (is_same::value) + ? dt_infinite + : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min + = (is_same::value) + ? 0.f + : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] + = static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max + : (distance_transform < dst_channel_min) ? dst_channel_min + : distance_transform); + } + } + } +} + +/// \breif Calculates one-dimensional squared euclidean distance. +/// Reference - http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf (Section 2.1) +template +std::vector compute_1d_squared_euclidean_distance_transform( + ImageViewIterator f, + std::ptrdiff_t n) +{ + std::ptrdiff_t k = 0; + std::vector v(n); + std::vector z(n + 1); + v[0] = 0; + z[0] = -dt_infinite; + z[1] = dt_infinite; + for (std::ptrdiff_t q = 1; q < n; ++q) + { + float s = static_cast( + ((f[q][0] + std::pow(q, 2)) - (f[v[k]][0] + std::pow(v[k], 2))) / (2 * q - 2 * v[k])); + while (s <= z[k]) + { + k = k - 1; + s = static_cast( + ((f[q][0] + std::pow(q, 2)) - (f[v[k]][0] + std::pow(v[k], 2))) + / (2 * q - 2 * v[k])); + } + k = k + 1; + v[k] = q; + z[k] = s; + z[k + 1] = dt_infinite; + } + k = 0; + std::vector d_f(n); + for (std::ptrdiff_t q = 0; q < n; ++q) + { + while (z[k + 1] < q) + { + ++k; + } + d_f[q] = static_cast(std::pow((q - v[k]), 2) + f[v[k]][0]); + } + return d_f; +} + +/// \breif Calculates two dimensionsal (precise) euclidean distance transform. +/// Reference - http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf (Section 2.2) +template +void distance_transorm_precise_impl( + SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from) +{ + gray32f_image_t intermediate_image(src_view.dimensions()); + gray32f_view_t intermediate_image_view = view(intermediate_image); + float constexpr src_channel_min + = (std::numeric_limits::type>::min)(); + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if (dist_from == distance_from::off_pixels) + { + if (src_view(x, y)[0] == src_channel_min) + intermediate_image_view(x, y)[0] = 0; + else + intermediate_image_view(x, y)[0] = dt_infinite; + } + else + { + if (src_view(x, y)[0] != src_channel_min) + intermediate_image_view(x, y)[0] = 0; + else + intermediate_image_view(x, y)[0] = dt_infinite; + } + } + } + + // Computes one-dimensional distance transforms along each column of the grid (image). + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + { + typename gray32f_view_t::y_iterator intermediate_col_itr + = intermediate_image_view.col_begin(x); + + std::vector one_dimensional_sq_euclidean_dt_along_column + = compute_1d_squared_euclidean_distance_transform( + intermediate_col_itr, intermediate_image_view.height()); + + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + { + intermediate_col_itr[y][0] = one_dimensional_sq_euclidean_dt_along_column[y]; + } + } + + // Computes one-dimensional distance transforms along each row of the grid (image). + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + { + typename gray32f_view_t::x_iterator intermediate_row_itr + = intermediate_image_view.row_begin(y); + + std::vector one_dimensional_sq_euclidean_dt_along_row + = compute_1d_squared_euclidean_distance_transform( + intermediate_row_itr, intermediate_image_view.width()); + + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + { + intermediate_row_itr[x][0] = one_dimensional_sq_euclidean_dt_along_row[x]; + + float const distance_transform = std::sqrt(intermediate_row_itr[x][0]); + + float constexpr dst_channel_max + = (is_same::value) + ? dt_infinite + : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min + = (is_same::value) + ? 0.f + : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] + = static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max + : (distance_transform < dst_channel_min) ? dst_channel_min + : distance_transform); + } + } +} + +} // namespace detail + +/// \addtogroup ImageProcessing +/// @{ +/// \breif Calculates distance to the nearest 'on' OR 'off' pixel (as specified), +/// for each pixel of a binary image. +/// +/// \param src_view - Source image view. +/// \param dst_view - Destination image view. +/// \param dist_from - Specifies where to find distance from, neareast, 'on' OR 'off' pixels. +/// \param dist_type - Specifies formula/method for distance calculation. +/// \tparam SrcView - Source image view type. +/// \tparam DstView - Destination image view type. +/// \tparam DistanceType - Distance type choosen. +/// \tparam MaskSize - Mask size choosen. +template +void distance_transform( + SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from, + DistanceType dist_type, + MaskSize /*Passes type*/) +{ + gil_function_requires>(); + gil_function_requires>(); + + static_assert( + std::is_same::type>::value + && std::is_same::type>::value, + "Source and destination image views must use gray color space."); + + static_assert( + detail::dt_check_distance_type::value, "Distance type not recognized."); + static_assert(detail::dt_check_mask_size::value, "Mask Size not recognized."); + + // Generates compile time error if invalid combination of distance_type and mask_size is used. + static_assert( + detail::dt_parameters_are_compatible::value, + "distance_transform is incompatible with combination of " + "distance_type and mask_size used."); + + BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + + if (std::is_same::value) + distance_transform_mask_size_three_impl(src_view, dst_view, dist_from, dist_type); + + else if (std::is_same::value) + distance_transform_mask_size_five_impl(src_view, dst_view, dist_from, dist_type); + + else + distance_transorm_precise_impl(src_view, dst_view, dist_from); +} +/// @} +}} // namespace boost::gil +#endif // BOOST_GIL_IMAGE_PROCESSING_DISTANCE_TRANSFORM_HPP diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index 1581fd3075..a8d47eacdc 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -10,7 +10,8 @@ foreach(_name threshold_binary threshold_truncate threshold_otsu - morphology) + morphology + distance_transform) set(_test t_core_image_processing_${_name}) set(_target test_core_image_processing_${_name}) diff --git a/test/core/image_processing/Jamfile b/test/core/image_processing/Jamfile index acd74d08f2..43791c956c 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -23,3 +23,4 @@ run anisotropic_diffusion.cpp ; run hough_line_transform.cpp ; run hough_circle_transform.cpp ; run morphology.cpp ; +run distance_transform.cpp ; diff --git a/test/core/image_processing/distance_transform.cpp b/test/core/image_processing/distance_transform.cpp new file mode 100644 index 0000000000..effe58fe1d --- /dev/null +++ b/test/core/image_processing/distance_transform.cpp @@ -0,0 +1,1019 @@ +// +// Copyright 2021 Harshit Pant +// +// 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 + +#include + +#include + +namespace gil = boost::gil; + +template +void fill_image_view(std::vector>& values, ImgView image_view) +{ + for (std::ptrdiff_t y = 0; y < image_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < image_view.width(); ++x) + { + image_view(x, y)[0] = values[y][x]; + } + } +} + +template +void check_for_negligible_varition(ImgView1 const& image_view1, ImgView2 const& image_view2) +{ + for (std::ptrdiff_t y = 0; y < image_view1.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < image_view1.width(); ++x) + { + float variation = std::abs(image_view1(x, y)[0] - image_view2(x, y)[0]); + BOOST_TEST_LT(variation, 0.001); + } + } +} + +void test_manhattan_uint8_t_input_uint8_t_output_distance_from_off_pixels() +{ + gil::gray8_image_t img_in(7, 7); + gil::gray8_image_t img_expected(7, 7); + gil::gray8_image_t img_out(7, 7); + + // test values from wikipedia, in 8 bit binary form + std::vector> val_in{ + {0, 0, 0, 0, 0, 0, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 0, 0, 0, 0, 0, 0}}; + + std::vector> val_exp{ + {0, 0, 0, 0, 0, 0, 0}, + {0, 1, 1, 1, 1, 1, 0}, + {0, 1, 2, 2, 2, 1, 0}, + {0, 1, 2, 3, 2, 1, 0}, + {0, 1, 2, 2, 2, 1, 0}, + {0, 1, 1, 1, 1, 1, 0}, + {0, 0, 0, 0, 0, 0, 0}}; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp, view(img_expected)); + + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::manhattan, + gil::mask_size::three); + + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); +} + + +void test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels() +{ + gil::gray16_image_t img_in(7, 7); + gil::gray8_image_t img_expected(7, 7); + gil::gray8_image_t img_out(7, 7); + gil::gray32f_image_t img_expected_float32_t(7, 7); + gil::gray32f_image_t img_out_float32_t(7, 7); + + std::vector> val_in{ + {0, 65535, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 65535, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}}; + + std::vector> val_exp_chessboard_three{ + {1, 0, 1, 2, 3, 4, 4}, + {1, 1, 1, 2, 3, 3, 3}, + {2, 2, 2, 2, 2, 2, 3}, + {3, 2, 1, 1, 1, 2, 3}, + {3, 2, 1, 0, 1, 2, 3}, + {3, 2, 1, 1, 1, 2, 3}, + {3, 2, 2, 2, 2, 2, 3}}; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp_chessboard_three, view(img_expected)); + + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::on_pixels, + gil::distance_type::chessboard, + gil::mask_size::three); + + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + std::vector> val_exp_manhattan_five{ + {1, 0, 1, 2, 3, 4, 5}, + {2, 1, 2, 3, 4, 5, 6}, + {3, 2, 3, 2, 3, 4, 5}, + {4, 3, 2, 1, 2, 3, 4}, + {3, 2, 1, 0, 1, 2, 3}, + {4, 3, 2, 1, 2, 3, 4}, + {5, 4, 3, 2, 3, 4, 5}}; + + fill_image_view(val_exp_manhattan_five, view(img_expected)); + + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::on_pixels, + gil::distance_type::manhattan, + gil::mask_size::five); + + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + std::vector> val_exp_euclidean_precise{ + {1, 0, 1, 2, 3, 4, 5}, + {1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 3.6055512f, 4.2426405f}, + {2.236068f, 2, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f}, + {3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f}, + {3, 2, 1, 0, 1, 2, 3}, + {3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f}, + {3.6055512f, 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f}}; + + fill_image_view(val_exp_euclidean_precise, view(img_expected_float32_t)); + + gil::distance_transform( + view(img_in), + view(img_out_float32_t), + gil::distance_from::on_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); + + BOOST_TEST(equal_pixels(const_view(img_out_float32_t), const_view(img_expected_float32_t))); +} + +void test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance_from_off_pixels() +{ + gil::gray8_image_t img_in(15, 15); + gil::gray32f_image_t img_expected(15, 15); + gil::gray32f_image_t img_out(15, 15); + + std::vector> val_in{ + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}; + + std::vector> val_exp1{ + {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + {1, 0, 1, 2, 1.4f, 1, 1, 1, 1, 1, 1.4f, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1.4f, 1, 1, 1, 1, 1, 1.4f, 2, 1, 0, 1}, + {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f}}; + + std::vector> val_exp2{ + {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + {1, 0, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 0, 1}, + {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}}; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp1, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::five); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + fill_image_view(val_exp2, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::manhattan, + gil::mask_size::five); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); +} + +void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() +{ + gil::gray8_image_t img_in(15, 15); + gil::gray32f_image_t img_expected(15, 15); + gil::gray32f_image_t img_out(15, 15); + + std::vector> val_in{ + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0}}; + + std::vector> val_exp_precise{ + {3.6055512f, + 3.1622777f, + 3, + 3.1622777f, + 3.6055512f, + 4.2426405f, + 4.1231055f, + 3.1622777f, + 2.236068f, + 1.4142135f, + 1, + 1.4142135f, + 2.236068f, + 3.1622777f, + 4.1231055f}, + {2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2.236068f, + 1.4142135f, + 1, + 1.4142135f, + 2.236068f, + 3.1622777f, + 4.1231055f, + 3.1622777f, + 2.236068f, + 1.4142135f, + 1, + 1.4142135f, + 2.236068f, + 3.1622777f, + 4.1231055f}, + {2, + 1, + 0, + 1, + 2, + 3, + 4, + 3.6055512f, + 2.828427f, + 2.236068f, + 2, + 2.236068f, + 2.828427f, + 3.6055512f, + 4.472136f}, + {2.236068f, + 1.4142135f, + 1, + 1.4142135f, + 2.236068f, + 3.1622777f, + 4.1231055f, + 4.2426405f, + 3.6055512f, + 3.1622777f, + 3, + 3.1622777f, + 3.6055512f, + 4.2426405f, + 5}, + {2.828427f, + 2.236068f, + 2, + 2.236068f, + 2.828427f, + 3.6055512f, + 4.472136f, + 5, + 4.472136f, + 4.1231055f, + 4, + 4.1231055f, + 4.472136f, + 5, + 5.656854f}, + {3.6055512f, + 3.1622777f, + 3, + 3.1622777f, + 3.6055512f, + 4.2426405f, + 5, + 5.8309517f, + 5.3851647f, + 5.0990195f, + 5, + 5.0990195f, + 5.3851647f, + 5.8309517f, + 6.4031243f}, + {4.472136f, + 4.1231055f, + 4, + 4.1231055f, + 4.472136f, + 5, + 5.656854f, + 6.4031243f, + 6.3245554f, + 6.0827622f, + 6, + 6.0827622f, + 6.3245554f, + 6.7082043f, + 7.0000005f}, + {5.3851647f, + 5.0990195f, + 5, + 5.0990195f, + 5.3851647f, + 5.8309517f, + 6.4031243f, + 7.0710683f, + 7.2801094f, + 7.0710683f, + 7.0000005f, + 6.7082043f, + 6.3245554f, + 6.0827622f, + 6}, + {6.3245554f, + 6.0827622f, + 6, + 6.0827622f, + 6.3245554f, + 6.7082043f, + 7.2111025f, + 7.8102503f, + 7.8102503f, + 7.0710683f, + 6.4031243f, + 5.8309517f, + 5.3851647f, + 5.0990195f, + 5}, + {7.2801094f, + 7.0710683f, + 7.0000005f, + 7.0710683f, + 7.2801094f, + 7.6157737f, + 8.062258f, + 8.062258f, + 7.2111025f, + 6.4031243f, + 5.656854f, + 5, + 4.472136f, + 4.1231055f, + 4}, + {8.246211f, + 8.062258f, + 8, + 8.062258f, + 8.246211f, + 8.5440035f, + 8.5440035f, + 7.6157737f, + 6.7082043f, + 5.8309517f, + 5, + 4.2426405f, + 3.6055512f, + 3.1622777f, + 3}, + {9.219544f, + 9.055386f, + 9, + 9.055386f, + 9.219544f, + 9.219544f, + 8.246211f, + 7.2801094f, + 6.3245554f, + 5.3851647f, + 4.472136f, + 3.6055512f, + 2.828427f, + 2.236068f, + 2}, + {10.198039f, + 10.049875f, + 10, + 10.049875f, + 10.049875f, + 9.055386f, + 8.062258f, + 7.0710683f, + 6.0827622f, + 5.0990195f, + 4.1231055f, + 3.1622777f, + 2.236068f, + 1.4142135f, + 1}, + {11.180341f, 11.045361f, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp_precise, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); + + check_for_negligible_varition(view(img_out), view(img_expected)); + + std::vector> val_exp_euclidean_three{ + {3.6935883f, + 3.2792969f, + 2.8650055f, + 3.2792969f, + 3.6935883f, + 4.1078796f, + 4.2342987f, + 3.2792969f, + 2.324295f, + 1.3692932f, + 0.95500183f, + 1.3692932f, + 2.324295f, + 3.2792969f, + 4.2342987f}, + {2.7385864f, + 2.324295f, + 1.9100037f, + 2.324295f, + 2.7385864f, + 3.6935883f, + 3.8200073f, + 2.8650055f, + 1.9100037f, + 0.95500183f, + 0, + 0.95500183f, + 1.9100037f, + 2.8650055f, + 3.8200073f}, + {2.324295f, + 1.3692932f, + 0.95500183f, + 1.3692932f, + 2.324295f, + 3.2792969f, + 4.2342987f, + 3.2792969f, + 2.324295f, + 1.3692932f, + 0.95500183f, + 1.3692932f, + 2.324295f, + 3.2792969f, + 4.2342987f}, + {1.9100037f, + 0.95500183f, + 0, + 0.95500183f, + 1.9100037f, + 2.8650055f, + 3.8200073f, + 3.6935883f, + 2.7385864f, + 2.324295f, + 1.9100037f, + 2.324295f, + 2.7385864f, + 3.6935883f, + 4.64859f}, + {2.324295f, + 1.3692932f, + 0.95500183f, + 1.3692932f, + 2.324295f, + 3.2792969f, + 4.2342987f, + 4.1078796f, + 3.6935883f, + 3.2792969f, + 2.8650055f, + 3.2792969f, + 3.6935883f, + 4.1078796f, + 5.0628815f}, + {2.7385864f, + 2.324295f, + 1.9100037f, + 2.324295f, + 2.7385864f, + 3.6935883f, + 4.64859f, + 5.0628815f, + 4.64859f, + 4.2342987f, + 3.8200073f, + 4.2342987f, + 4.64859f, + 5.0628815f, + 5.477173f}, + {3.6935883f, + 3.2792969f, + 2.8650055f, + 3.2792969f, + 3.6935883f, + 4.1078796f, + 5.0628815f, + 6.0178833f, + 5.603592f, + 5.1893005f, + 4.775009f, + 5.1893005f, + 5.603592f, + 6.0178833f, + 6.4321747f}, + {4.64859f, + 4.2342987f, + 3.8200073f, + 4.2342987f, + 4.64859f, + 5.0628815f, + 5.477173f, + 6.4321747f, + 6.5585938f, + 6.1443024f, + 5.730011f, + 6.1443024f, + 6.5585938f, + 6.972885f, + 6.685013f}, + {5.603592f, + 5.1893005f, + 4.775009f, + 5.1893005f, + 5.603592f, + 6.0178833f, + 6.4321747f, + 6.846466f, + 7.5135956f, + 7.099304f, + 6.685013f, + 6.972885f, + 6.5585938f, + 6.1443024f, + 5.730011f}, + {6.5585938f, + 6.1443024f, + 5.730011f, + 6.1443024f, + 6.5585938f, + 6.972885f, + 7.3871765f, + 7.801468f, + 7.801468f, + 6.846466f, + 6.4321747f, + 6.0178833f, + 5.603592f, + 5.1893005f, + 4.775009f}, + {7.5135956f, + 7.099304f, + 6.685013f, + 7.099304f, + 7.5135956f, + 7.927887f, + 8.342178f, + 8.342178f, + 7.3871765f, + 6.4321747f, + 5.477173f, + 5.0628815f, + 4.64859f, + 4.2342987f, + 3.8200073f}, + {8.468597f, + 8.054306f, + 7.6400146f, + 8.054306f, + 8.468597f, + 8.882889f, + 8.882889f, + 7.927887f, + 6.972885f, + 6.0178833f, + 5.0628815f, + 4.1078796f, + 3.6935883f, + 3.2792969f, + 2.8650055f}, + {9.423599f, + 9.009308f, + 8.5950165f, + 9.009308f, + 9.423599f, + 9.423599f, + 8.468597f, + 7.5135956f, + 6.5585938f, + 5.603592f, + 4.64859f, + 3.6935883f, + 2.7385864f, + 2.324295f, + 1.9100037f}, + {10.378601f, + 9.96431f, + 9.550018f, + 9.96431f, + 9.96431f, + 9.009308f, + 8.054306f, + 7.099304f, + 6.1443024f, + 5.1893005f, + 4.2342987f, + 3.2792969f, + 2.324295f, + 1.3692932f, + 0.95500183f}, + {11.333603f, + 10.919312f, + 10.50502f, + 10.50502f, + 9.550018f, + 8.5950165f, + 7.6400146f, + 6.685013f, + 5.730011f, + 4.775009f, + 3.8200073f, + 2.8650055f, + 1.9100037f, + 0.95500183f, + 0.f}}; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp_euclidean_three, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::three); + + check_for_negligible_varition(view(img_out), view(img_expected)); + + std::vector> val_exp_euclidean_five{ + {3.5969f, + 3.1969f, + 3, + 3.1969f, + 3.5969f, + 4.2f, + 4.1969f, + 3.1969f, + 2.1969f, + 1.4f, + 1, + 1.4f, + 2.1969f, + 3.1969f, + 4.1969f}, + {2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2.1969f, + 1.4f, + 1, + 1.4f, + 2.1969f, + 3.1969f, + 4.1969f, + 3.1969f, + 2.1969f, + 1.4f, + 1, + 1.4f, + 2.1969f, + 3.1969f, + 4.1969f}, + {2, 1, 0, 1, 2, 3, 4, 3.5969f, 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, 4.3938f}, + {2.1969f, + 1.4f, + 1, + 1.4f, + 2.1969f, + 3.1969f, + 4.1969f, + 4.2f, + 3.5969f, + 3.1969f, + 3, + 3.1969f, + 3.5969f, + 4.2f, + 4.9968996f}, + {2.8f, + 2.1969f, + 2, + 2.1969f, + 2.8f, + 3.5969f, + 4.3938f, + 4.9968996f, + 4.3938f, + 4.1969f, + 4, + 4.1969f, + 4.3938f, + 4.9968996f, + 5.6f}, + {3.5969f, + 3.1969f, + 3, + 3.1969f, + 3.5969f, + 4.2f, + 4.9968996f, + 5.7938f, + 5.3938f, + 5.1969f, + 5, + 5.1969f, + 5.3938f, + 5.7938f, + 6.3968997f}, + {4.3938f, + 4.1969f, + 4, + 4.1969f, + 4.3938f, + 4.9968996f, + 5.6f, + 6.3968997f, + 6.3938f, + 6.1969f, + 6, + 6.1969f, + 6.3938f, + 6.5906997f, + 7}, + {5.3938f, + 5.1969f, + 5, + 5.1969f, + 5.3938f, + 5.7938f, + 6.3968997f, + 7, + 7.3938f, + 7.1969f, + 7, + 6.5906997f, + 6.3938f, + 6.1969f, + 6}, + {6.3938f, + 6.1969f, + 6, + 6.1969f, + 6.3938f, + 6.5906997f, + 7.1937995f, + 7.7969f, + 7.7969f, + 7, + 6.3968997f, + 5.7938f, + 5.3938f, + 5.1969f, + 5}, + {7.3938f, + 7.1969f, + 7, + 7.1969f, + 7.3938f, + 7.5906997f, + 7.9907f, + 7.9907f, + 7.1937995f, + 6.3968997f, + 5.6f, + 4.9968996f, + 4.3938f, + 4.1969f, + 4}, + {8.3938f, + 8.196899f, + 8, + 8.196899f, + 8.3938f, + 8.5907f, + 8.5907f, + 7.5906997f, + 6.5906997f, + 5.7938f, + 4.9968996f, + 4.2f, + 3.5969f, + 3.1969f, + 3}, + {9.3938f, + 9.196899f, + 9, + 9.196899f, + 9.3938f, + 9.3938f, + 8.3938f, + 7.3938f, + 6.3938f, + 5.3938f, + 4.3938f, + 3.5969f, + 2.8f, + 2.1969f, + 2}, + {10.393799f, + 10.196899f, + 10, + 10.196899f, + 10.196899f, + 9.196899f, + 8.196899f, + 7.1969f, + 6.1969f, + 5.1969f, + 4.1969f, + 3.1969f, + 2.1969f, + 1.4f, + 1}, + {11.393799f, 11.196899f, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp_euclidean_five, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::five); + + check_for_negligible_varition(view(img_out), view(img_expected)); + + + std::vector> val_exp_manhatten_three{ + {5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, + {3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, + {4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, + {5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, + {6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, + {7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, + {8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, + {9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, + {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, + {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, + {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, + {13, 12, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; + + fill_image_view(val_exp_manhatten_three, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::manhattan, + gil::mask_size::three); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + + std::vector> val_exp_manhatten_five{ + {5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, + {3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, + {4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, + {5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, + {6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, + {7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, + {8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, + {9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, + {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, + {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, + {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, + {13, 12, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; + + fill_image_view(val_exp_manhatten_five, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::manhattan, + gil::mask_size::five); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + + std::vector> val_exp_chessboard_three{ + {3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + {3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, + {4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, + {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, + {6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, + {7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, + {8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, + {9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, + {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, + {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; + + fill_image_view(val_exp_chessboard_three, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::chessboard, + gil::mask_size::three); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + std::vector> val_exp_chessboard_five{ + {3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + {3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, + {4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, + {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, + {6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, + {7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, + {8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, + {9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, + {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, + {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; + + fill_image_view(val_exp_chessboard_five, view(img_expected)); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::chessboard, + gil::mask_size::five); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); +} + +int main() +{ + test_manhattan_uint8_t_input_uint8_t_output_distance_from_off_pixels(); + test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels(); + test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance_from_off_pixels(); + test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels(); + return ::boost::report_errors(); +}