diff --git a/include/boost/gil/extension/numeric/kernel.hpp b/include/boost/gil/extension/numeric/kernel.hpp index 5fbb8b046d..9d95d33388 100644 --- a/include/boost/gil/extension/numeric/kernel.hpp +++ b/include/boost/gil/extension/numeric/kernel.hpp @@ -9,6 +9,7 @@ #define BOOST_GIL_EXTENSION_NUMERIC_KERNEL_HPP #include +#include #include @@ -17,6 +18,7 @@ #include #include #include +#include namespace boost { namespace gil { @@ -151,6 +153,206 @@ inline Kernel reverse_kernel(Kernel const& kernel) return result; } + +namespace detail { + +template +class kernel_2d_adaptor : public Core +{ +public: + kernel_2d_adaptor() = default; + + explicit kernel_2d_adaptor(std::size_t center_vertical, std::size_t center_horizontal) + : center_(center_horizontal, center_vertical) + { + BOOST_ASSERT(center_.y < this->size() && center_.x < this->size()); + } + + kernel_2d_adaptor(std::size_t size, std::size_t center_vertical, std::size_t center_horizontal) + : Core(size * size), + square_size(size), + center_(center_horizontal, center_vertical) + { + BOOST_ASSERT(this->size() > 0); + BOOST_ASSERT(center_.y < this->size() && center_.x < this->size()); // also implies `size() > 0` + } + + kernel_2d_adaptor(kernel_2d_adaptor const& other) + : Core(other), + square_size(other.square_size), + center_(other.center_.x, other.center_.y) + { + BOOST_ASSERT(this->size() > 0); + BOOST_ASSERT(center_.y < this->size() && center_.x < this->size()); // also implies `size() > 0` + } + + kernel_2d_adaptor& operator=(kernel_2d_adaptor const& other) + { + Core::operator=(other); + center_.y = other.center_.y; + center_.x = other.center_.x; + square_size = other.square_size; + return *this; + } + + std::size_t upper_size() const + { + BOOST_ASSERT(center_.y < this->size()); + return center_.y; + } + + std::size_t lower_size() const + { + BOOST_ASSERT(center_.y < this->size()); + return this->size() - center_.y - 1; + } + + std::size_t left_size() const + { + BOOST_ASSERT(center_.x < this->size()); + return center_.x; + } + + std::size_t right_size() const + { + BOOST_ASSERT(center_.x < this->size()); + return this->size() - center_.x - 1; + } + + auto center_vertical() -> std::size_t& + { + BOOST_ASSERT(center_.y < this->size()); + return center_.y; + } + + auto center_vertical() const -> std::size_t const& + { + BOOST_ASSERT(center_.y < this->size()); + return center_.y; + } + + auto center_horizontal() -> std::size_t& + { + BOOST_ASSERT(center_.x < this->size()); + return center_.x; + } + + auto center_horizontal() const -> std::size_t const& + { + BOOST_ASSERT(center_.x < this->size()); + return center_.x; + } + + std::size_t size() const + { + return square_size; + } + + typename Core::value_type at(std::size_t x, std::size_t y) const + { + if (x >= this->size() || y >= this->size()) + { + throw std::out_of_range("Index out of range"); + } + + return this->begin()[y * this->size() + x]; + } + +private: + using center_t = point; + center_t center_{ 0, 0 }; + +protected: + std::size_t square_size{ 0 }; +}; +} //namespace detail + +/// \brief variable-size kernel +template +< + typename T, + typename Allocator = std::allocator +> +class kernel_2d : public detail::kernel_2d_adaptor> +{ + using parent_t = detail::kernel_2d_adaptor>; + +public: + + kernel_2d() = default; + kernel_2d( + std::size_t size, + std::size_t vertical_center, + std::size_t center_horizontal + ) : parent_t(size, vertical_center, center_horizontal){} + + template + kernel_2d( + FwdIterator elements, + std::size_t size, + std::size_t vertical_center, + std::size_t center_horizontal + ) : parent_t(static_cast(std::sqrt(size)), vertical_center, center_horizontal) + { + detail::copy_n(elements, size, this->begin()); + } + + kernel_2d(kernel_2d const& other) : parent_t(other) {} + kernel_2d& operator=(kernel_2d const& other) = default; +}; + +/// \brief static-size kernel +template +class kernel_2d_fixed : + public detail::kernel_2d_adaptor> +{ + using parent_t = detail::kernel_2d_adaptor>; +public: + static constexpr std::size_t static_size = Size; + static_assert(static_size > 0, "kernel must have size greater than 0"); + static_assert(static_size % 2 == 1, "kernel size must be odd to ensure validity at the center"); + + kernel_2d_fixed() + { + this->square_size = Size; + } + + explicit kernel_2d_fixed(std::size_t center_vertical, std::size_t center_horizontal) : + parent_t(center_vertical, center_horizontal) + { + this->square_size = Size; + } + + template + explicit kernel_2d_fixed( + FwdIterator elements, + std::size_t center_vertical, + std::size_t center_horizontal + ) : parent_t(center_vertical, center_horizontal) + { + this->square_size = Size; + detail::copy_n(elements, Size * Size, this->begin()); + } + + kernel_2d_fixed(kernel_2d_fixed const& other) : parent_t(other) {} + kernel_2d_fixed& operator=(kernel_2d_fixed const& other) = default; +}; + +// TODO: This data member is odr-used and definition at namespace scope +// is required by C++11. Redundant and deprecated in C++17. +template +constexpr std::size_t kernel_2d_fixed::static_size; + +/// \brief reverse a kernel +//template +//inline Kernel reverse_kernel(Kernel const& kernel) +//{ +// Kernel result(kernel); +// result.center() = kernel.right_size(); +// std::reverse(result.begin(), result.end()); +// return result; +//} + }} // namespace boost::gil #endif diff --git a/test/extension/numeric/kernel.cpp b/test/extension/numeric/kernel.cpp index 37297dd19e..0c699e8613 100644 --- a/test/extension/numeric/kernel.cpp +++ b/test/extension/numeric/kernel.cpp @@ -24,6 +24,20 @@ BOOST_AUTO_TEST_CASE(kernel_1d_default_constructor) BOOST_TEST(k.size() == 0); } +BOOST_AUTO_TEST_CASE(kernel_2d_default_constructor) +{ + gil::kernel_2d k; + BOOST_TEST(k.center_vertical() == 0); + BOOST_TEST(k.center_horizontal() == 0); + + //BOOST_TEST(k.left_size() == 0); + //BOOST_TEST(k.right_size() == -1); + BOOST_TEST(k.upper_size() == 0); + BOOST_TEST(k.lower_size() == -1); + // std::vector interface + BOOST_TEST(k.size() == 0); +} + BOOST_AUTO_TEST_CASE(kernel_1d_parameterized_constructor) { gil::kernel_1d k(9, 4); @@ -34,6 +48,19 @@ BOOST_AUTO_TEST_CASE(kernel_1d_parameterized_constructor) BOOST_TEST(k.size() == 9); } +BOOST_AUTO_TEST_CASE(kernel_2d_parameterized_constructor) +{ + gil::kernel_2d k(9, 4, 4); + BOOST_TEST(k.center_vertical() == 4); + BOOST_TEST(k.center_horizontal() == 4); + BOOST_TEST(k.left_size() == 4); + BOOST_TEST(k.right_size() == 4); + BOOST_TEST(k.upper_size() == 4); + BOOST_TEST(k.lower_size() == 4); + // std::vector interface + BOOST_TEST(k.size() == 9); +} + BOOST_AUTO_TEST_CASE(kernel_1d_parameterized_constructor_with_iterator) { std::vector v(9); @@ -45,6 +72,20 @@ BOOST_AUTO_TEST_CASE(kernel_1d_parameterized_constructor_with_iterator) BOOST_TEST(k.size() == 9); } +BOOST_AUTO_TEST_CASE(kernel_2d_parameterized_constructor_with_iterator) +{ + std::vector v(81); + gil::kernel_2d k(v.cbegin(), v.size(), 4, 4); + BOOST_TEST(k.center_vertical() == 4); + BOOST_TEST(k.center_horizontal() == 4); + BOOST_TEST(k.left_size() == 4); + BOOST_TEST(k.right_size() == 4); + BOOST_TEST(k.upper_size() == 4); + BOOST_TEST(k.lower_size() == 4); + // std::vector interface + BOOST_TEST(k.size() == 9); +} + BOOST_AUTO_TEST_CASE(kernel_1d_copy_constructor) { gil::kernel_1d d(9, 4); @@ -57,6 +98,22 @@ BOOST_AUTO_TEST_CASE(kernel_1d_copy_constructor) BOOST_TEST(k.size() == d.size()); } +BOOST_AUTO_TEST_CASE(kernel_2d_copy_constructor) +{ + gil::kernel_2d d(9, 4, 4); + gil::kernel_2d k(d); + BOOST_TEST(k.center_vertical() == 4); + BOOST_TEST(k.center_horizontal() == 4); + BOOST_TEST(k.center_vertical() == d.center_vertical()); + BOOST_TEST(k.center_horizontal() == d.center_horizontal()); + BOOST_TEST(k.left_size() == d.left_size()); + BOOST_TEST(k.right_size() == d.right_size()); + BOOST_TEST(k.lower_size() == d.lower_size()); + BOOST_TEST(k.upper_size() == d.upper_size()); + // std::vector interface + BOOST_TEST(k.size() == d.size()); +} + BOOST_AUTO_TEST_CASE(kernel_1d_assignment_operator) { gil::kernel_1d d(9, 4); @@ -70,6 +127,23 @@ BOOST_AUTO_TEST_CASE(kernel_1d_assignment_operator) BOOST_TEST(k.size() == d.size()); } +BOOST_AUTO_TEST_CASE(kernel_2d_assignment_operator) +{ + gil::kernel_2d d(9, 4, 4); + gil::kernel_2d k; + k = d; + BOOST_TEST(k.center_vertical() == 4); + BOOST_TEST(k.center_horizontal() == 4); + BOOST_TEST(k.center_vertical() == d.center_vertical()); + BOOST_TEST(k.center_horizontal() == d.center_horizontal()); + BOOST_TEST(k.left_size() == d.left_size()); + BOOST_TEST(k.right_size() == d.right_size()); + BOOST_TEST(k.lower_size() == d.lower_size()); + BOOST_TEST(k.upper_size() == d.upper_size()); + // std::vector interface + BOOST_TEST(k.size() == d.size()); +} + BOOST_AUTO_TEST_CASE(kernel_1d_reverse_Kernel) { gil::kernel_1d d(12, 4); @@ -90,6 +164,19 @@ BOOST_AUTO_TEST_CASE(kernel_1d_fixed_default_constructor) BOOST_TEST(k.size() == 9); } +BOOST_AUTO_TEST_CASE(kernel_2d_fixed_default_constructor) +{ + gil::kernel_2d_fixed k; + BOOST_TEST(k.center_horizontal() == 0); + BOOST_TEST(k.center_vertical() == 0); + BOOST_TEST(k.left_size() == 0); + BOOST_TEST(k.right_size() == 8); // TODO: Why not 0 or -1 if not set? + BOOST_TEST(k.upper_size() == 0); + BOOST_TEST(k.lower_size() == 8); + // std::array interface + BOOST_TEST(k.size() == 9); +} + BOOST_AUTO_TEST_CASE(kernel_1d_fixed_parameterized_constructor) { gil::kernel_1d_fixed k(4); @@ -100,6 +187,19 @@ BOOST_AUTO_TEST_CASE(kernel_1d_fixed_parameterized_constructor) BOOST_TEST(k.size() == 9); } +BOOST_AUTO_TEST_CASE(kernel_2d_fixed_parameterized_constructor) +{ + gil::kernel_2d_fixed k(4, 4); + BOOST_TEST(k.center_horizontal() == 4); + BOOST_TEST(k.center_vertical() == 4); + BOOST_TEST(k.left_size() == 4); + BOOST_TEST(k.right_size() == 4); + BOOST_TEST(k.upper_size() == 4); + BOOST_TEST(k.lower_size() == 4); + // std::vector interface + BOOST_TEST(k.size() == 9); +} + BOOST_AUTO_TEST_CASE(kernel_1d_fixed_parameterized_constructor_with_iterator) { // FIXME: The constructor should throw if v.size() < k.size() @@ -113,6 +213,22 @@ BOOST_AUTO_TEST_CASE(kernel_1d_fixed_parameterized_constructor_with_iterator) BOOST_TEST(k.size() == 9); } +BOOST_AUTO_TEST_CASE(kernel_2d_fixed_parameterized_constructor_with_iterator) +{ +// // FIXME: The constructor should throw if v.size() < k.size() + std::array v; + gil::kernel_2d_fixed k(v.cbegin(), 4, 4); + BOOST_TEST((gil::kernel_2d_fixed::static_size) == 9); + BOOST_TEST(k.center_vertical() == 4); + BOOST_TEST(k.center_horizontal() == 4); + BOOST_TEST(k.left_size() == 4); + BOOST_TEST(k.right_size() == 4); + BOOST_TEST(k.upper_size() == 4); + BOOST_TEST(k.lower_size() == 4); + // std::vector interface + BOOST_TEST(k.size() == 9); +} + BOOST_AUTO_TEST_CASE(kernel_1d_fixed_copy_constructor) { gil::kernel_1d_fixed d(4); @@ -126,6 +242,23 @@ BOOST_AUTO_TEST_CASE(kernel_1d_fixed_copy_constructor) BOOST_TEST(k.size() == d.size()); } +BOOST_AUTO_TEST_CASE(kernel_2d_fixed_copy_constructor) +{ + gil::kernel_2d_fixed d(4, 4); + gil::kernel_2d_fixed k(d); + BOOST_TEST((gil::kernel_2d_fixed::static_size) == 9); + BOOST_TEST(k.center_horizontal() == 4); + BOOST_TEST(k.center_vertical() == 4); + BOOST_TEST(k.center_horizontal() == d.center_horizontal()); + BOOST_TEST(k.center_vertical() == d.center_vertical()); + BOOST_TEST(k.left_size() == d.left_size()); + BOOST_TEST(k.right_size() == d.right_size()); + BOOST_TEST(k.lower_size() == d.lower_size()); + BOOST_TEST(k.upper_size() == d.upper_size()); + // std::vector interface + BOOST_TEST(k.size() == d.size()); +} + BOOST_AUTO_TEST_CASE(kernel_1d_fixed_assignment_operator) { gil::kernel_1d_fixed d(4); @@ -140,6 +273,24 @@ BOOST_AUTO_TEST_CASE(kernel_1d_fixed_assignment_operator) BOOST_TEST(k.size() == d.size()); } +BOOST_AUTO_TEST_CASE(kernel_2d_fixed_assignment_operator) +{ + gil::kernel_2d_fixed d(4, 4); + gil::kernel_2d_fixed k; + k = d; + BOOST_TEST((gil::kernel_2d_fixed::static_size) == 9); + BOOST_TEST(k.center_horizontal() == 4); + BOOST_TEST(k.center_vertical() == 4); + BOOST_TEST(k.center_horizontal() == d.center_horizontal()); + BOOST_TEST(k.center_vertical() == d.center_vertical()); + BOOST_TEST(k.left_size() == d.left_size()); + BOOST_TEST(k.right_size() == d.right_size()); + BOOST_TEST(k.lower_size() == d.lower_size()); + BOOST_TEST(k.upper_size() == d.upper_size()); + // std::vector interface + BOOST_TEST(k.size() == d.size()); +} + BOOST_AUTO_TEST_CASE(kernel_1d_fixed_reverse_Kernel) { std::array values = {1, 2, 3}; @@ -151,3 +302,5 @@ BOOST_AUTO_TEST_CASE(kernel_1d_fixed_reverse_Kernel) auto k = gil::reverse_kernel(d); BOOST_TEST(k == values_rev); } + +