From 8b1c2d3ea4eb5bf0a1e7e093862962313dd6c2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 10 Nov 2020 09:14:10 +0100 Subject: [PATCH 001/193] Boost 1.75 release notes: plan to phase out GCC 5 and introduce C++14 See discussion in https://github.com/boostorg/gil/pull/526 --- RELEASES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index f759b37272..d006546324 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,6 +3,10 @@ All notable changes to [Boost.GIL](https://github.com/boostorg/gil/) project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.75.0] - 2020-12-09 + +BREAKING: In next release, we are going to drop support for GCC 5. We may also change the required minimum C++ version from C++11 to C++14. + ## [1.74.0] - 2020-08-12 ### Added From 6fc7d7541d1bb16b7cca1835129c13fe8ed8142c Mon Sep 17 00:00:00 2001 From: Mike-Devel Date: Sun, 22 Nov 2020 21:42:29 +0100 Subject: [PATCH 002/193] Removing unnecessary cast in hsv.cpp (#530) Both temp_red and max_color are float32_t so the result of the subraction already has the right type. This should also remove the only dependency on Boost.NumericConversion --- include/boost/gil/extension/toolbox/color_spaces/hsv.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/boost/gil/extension/toolbox/color_spaces/hsv.hpp b/include/boost/gil/extension/toolbox/color_spaces/hsv.hpp index a9b33dba51..2c79360e87 100644 --- a/include/boost/gil/extension/toolbox/color_spaces/hsv.hpp +++ b/include/boost/gil/extension/toolbox/color_spaces/hsv.hpp @@ -8,8 +8,6 @@ #ifndef BOOST_GIL_EXTENSION_TOOLBOX_COLOR_SPACES_HSV_HPP #define BOOST_GIL_EXTENSION_TOOLBOX_COLOR_SPACES_HSV_HPP -#include - #include #include #include @@ -86,7 +84,7 @@ struct default_color_converter_impl< rgb_t, hsv_t > } else { - if( (std::abs)( boost::numeric_cast(temp_red - max_color) ) < 0.0001f ) + if( (std::abs)( temp_red - max_color ) < 0.0001f ) { hue = ( temp_green - temp_blue ) / diff; From 09911cd1c44020e82cd8f14280f589512668bbaa Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Sun, 22 Nov 2020 22:49:46 +0100 Subject: [PATCH 003/193] ci: Boost.Numeric is no longer a dependency [ci skip] Follows up PR #530 --- .ci/get-boost.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.ci/get-boost.sh b/.ci/get-boost.sh index f0765f61d2..985731d7a9 100755 --- a/.ci/get-boost.sh +++ b/.ci/get-boost.sh @@ -44,7 +44,6 @@ git submodule --quiet update --init $GIT_SUBMODULE_OPTS \ libs/iterator \ libs/mp11 \ libs/mpl \ - libs/numeric/conversion \ libs/preprocessor \ libs/type_traits \ libs/variant2 From 8d049063035e86ad614215a7cbb67b2b99816266 Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Sun, 22 Nov 2020 22:54:31 +0100 Subject: [PATCH 004/193] docs: Update CONTRIBUTING.md [ci skip] --- CONTRIBUTING.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 248d4471a1..aae35c138f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -345,32 +345,33 @@ development environment. as they are executed. It is useful to inspect compilation flags. If no target or directory is specified, everything in the current directory -is built. For example, all Boost.GIL tests can be built and run using: +is built. For example, **all** Boost.GIL tests can be built and run using: ```shell cd libs/gil -../../b2 +b2 ``` Run core tests only specifying location of directory with tests: ```shell cd libs/gil -../../b2 cxxstd=11 test/core +b2 cxxstd=11 test/core ``` Run all tests for selected extension (from Boost root directory, as alternative): ```shell -./b2 cxxstd=11 libs/gil/test/io -./b2 cxxstd=11 libs/gil/test/numeric -./b2 cxxstd=11 libs/gil/test/toolbox +b2 cxxstd=11 libs/gil/test/extension/dynamic_image +b2 cxxstd=11 libs/gil/test/extension/io +b2 cxxstd=11 libs/gil/test/extension/numeric +b2 cxxstd=11 libs/gil/test/extension/toolbox ``` Run I/O extension tests bundled in target called `simple`: ```shell -./b2 cxxstd=11 libs/gil/test/io//simple +b2 cxxstd=11 libs/gil/test/io//simple ``` **TIP:** Pass `b2` feature `cxxstd=11,14,17,2a` to request compilation for From 2b1e4665d15bc5c58d657879ac52890611c7d77e Mon Sep 17 00:00:00 2001 From: Edward Diener Date: Wed, 16 Dec 2020 02:56:49 -0500 Subject: [PATCH 005/193] Add "cxxstd" json field (#531) The "cxxstd" json field is being added to each Boost library's meta json information for libraries whose minumum C++ standard compilation level is C++11 on up. The value of this field matches one of the values for 'cxxstd' in Boost.Build. The purpose of doing this is to provide information for the Boost website documentation for each library which will specify the minimum C++ standard compilation that an end-user must employ in order to use the particular library. This will aid end-users who want to know if they can successfully use a Boost library based on their C++ compiler's compilation level, without having to search the library's documentation to find this out. https://lists.boost.org/Archives/boost/2020/12/250543.php --- meta/libraries.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meta/libraries.json b/meta/libraries.json index 18c6c047d5..e72c3558d8 100644 --- a/meta/libraries.json +++ b/meta/libraries.json @@ -18,5 +18,6 @@ "Stefan Seefeld ", "Mateusz Loskot ", "Pranam Lashkari " - ] + ], + "cxxstd": "11" } From e1c69d1718b902d3ecb00d83ec4683494919b751 Mon Sep 17 00:00:00 2001 From: Debabrata Mandal <32168969+codejaeger@users.noreply.github.com> Date: Tue, 5 Jan 2021 00:17:01 +0530 Subject: [PATCH 006/193] Add codecov coverage (#532) --- .ci/coverage.sh | 7 +++++++ .travis.yml | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .ci/coverage.sh diff --git a/.ci/coverage.sh b/.ci/coverage.sh new file mode 100644 index 0000000000..42ff3e703f --- /dev/null +++ b/.ci/coverage.sh @@ -0,0 +1,7 @@ +#! /bin/bash +set -e +lcov --directory bin.v2 --capture --no-external --directory $(pwd) --output-file coverage.info > /dev/null 2>&1 +lcov --extract coverage.info $(pwd)'/boost/gil/*' --output-file coverage.info > /dev/null +lcov --list coverage.info +cd libs/gil +bash <(curl -s https://codecov.io/bash) -f ../../coverage.info || echo "Codecov did not collect coverage reports" diff --git a/.travis.yml b/.travis.yml index 3e46e5a11b..5c18d49687 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,22 @@ matrix: exclude: - env: BOGUS_JOB=true include: + - os: linux + env: COMPILER=g++-6 VARIANT=debug TOOLSET=gcc CXXSTD=11 B2_OPTIONS="coverage=on" + addons: + apt: + packages: + - g++-6 + - libpng-dev + - libjpeg-dev + - libtiff5-dev + - libraw-dev + - lcov + sources: + - ubuntu-toolchain-r-test + after_success: + - bash libs/gil/.ci/coverage.sh + - os: linux env: COMPILER=g++-5 VARIANT=debug TOOLSET=gcc CXXSTD=11 TEST_HEADERS=1 addons: From 0c0fe1ae79053d8aedfa95eca39329d135e4ea1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 11 Jan 2021 14:16:29 +0100 Subject: [PATCH 007/193] build: Switch conan packages from bincrafters to conan.io (#537) Discussion at https://github.com/conan-io/conan-center-index/issues/4151#issuecomment-757902106 Fixes Azure Pipelines builds for #535 --- conanfile.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/conanfile.txt b/conanfile.txt index f3f7c6304b..94558d7b19 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,14 +1,14 @@ # -# Copyright (c) 2018-2019 Mateusz Loskot +# Copyright (c) 2018-2021 Mateusz Loskot # # Distributed under 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) # [requires] -libpng/1.6.37@bincrafters/stable -libjpeg/9c@bincrafters/stable -libtiff/4.0.9@bincrafters/stable +libjpeg/9d +libpng/1.6.37 +libtiff/4.1.0 [generators] cmake From 68cdbdd2ec4e21f667a49ae1dbbc2cebd833051c Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 20 Jan 2021 23:09:58 +0100 Subject: [PATCH 008/193] Add inverse function for matrix3x2 (#527) --- .../boost/gil/extension/numeric/affine.hpp | 19 ++++++++++ test/extension/numeric/matrix3x2.cpp | 37 +++++++++++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/include/boost/gil/extension/numeric/affine.hpp b/include/boost/gil/extension/numeric/affine.hpp index ebef93afca..26054149a9 100644 --- a/include/boost/gil/extension/numeric/affine.hpp +++ b/include/boost/gil/extension/numeric/affine.hpp @@ -89,6 +89,25 @@ point transform(matrix3x2 const& mat, point const& src) return src * mat; } +/// Returns the inverse of the given affine transformation matrix +/// +/// \warning Floating point arithmetic, use Boost.Rational if precision maters +template +boost::gil::matrix3x2 inverse(boost::gil::matrix3x2 m) +{ + T const determinant = m.a * m.d - m.b * m.c; + + boost::gil::matrix3x2 res; + res.a = m.d / determinant; + res.b = -m.b / determinant; + res.c = -m.c / determinant; + res.d = m.a / determinant; + res.e = (m.c * m.f - m.d * m.e) / determinant; + res.f = (m.b * m.e - m.a * m.f) / determinant; + + return res; +} + }} // namespace boost::gil #endif diff --git a/test/extension/numeric/matrix3x2.cpp b/test/extension/numeric/matrix3x2.cpp index f1c19cd046..d3c34f4e48 100644 --- a/test/extension/numeric/matrix3x2.cpp +++ b/test/extension/numeric/matrix3x2.cpp @@ -16,8 +16,19 @@ namespace gil = boost::gil; -// FIXME: Remove when https://github.com/boostorg/core/issues/38 happens -#define BOOST_GIL_TEST_IS_CLOSE(a, b, epsilon) BOOST_TEST_LT(std::fabs((a) - (b)), (epsilon)) +// Tolerance predicate for floating point comparison to use with BOOST_TEST_WITH +template +struct with_tolerance +{ + with_tolerance(T tolerance) : tolerance(tolerance) {} + bool operator()(T lhs, T rhs) + { + return (std::abs(lhs - rhs) <= tolerance); + } + +private: + T tolerance; +}; namespace { constexpr double HALF_PI = 1.57079632679489661923; @@ -123,10 +134,10 @@ void test_matrix3x2_vector_multiplication() void test_matrix3x2_get_rotate() { auto m1 = gil::matrix3x2::get_rotate(HALF_PI); - BOOST_GIL_TEST_IS_CLOSE(m1.a, std::cos(HALF_PI), 0.03); + BOOST_TEST_WITH(m1.a, std::cos(HALF_PI), with_tolerance(0.03)); BOOST_TEST_EQ(m1.b, 1); BOOST_TEST_EQ(m1.c, -1); - BOOST_GIL_TEST_IS_CLOSE(m1.d, std::cos(HALF_PI), 0.03); + BOOST_TEST_WITH(m1.d, std::cos(HALF_PI), with_tolerance(0.03)); BOOST_TEST_EQ(m1.e, 0); BOOST_TEST_EQ(m1.f, 0); } @@ -173,6 +184,23 @@ void test_matrix3x2_transform() BOOST_TEST_EQ(v2.y, 4); } +void test_matrix3x2_inverse() +{ + using matrix_t = gil::matrix3x2; + using point_t = gil::point; + + matrix_t mo = matrix_t::get_translate(0, 16); + matrix_t mb = matrix_t::get_rotate(HALF_PI); + auto m = mo * mb; + + point_t p(10, 10); + point_t q = gil::transform(inverse(m), p); + point_t p2 = gil::transform(m, q); + + BOOST_TEST_WITH(p.x, p2.x, with_tolerance(1e-9)); + BOOST_TEST_WITH(p.y, p2.y, with_tolerance(1e-9)); +} + int main() { test_matrix3x2_default_constructor(); @@ -186,6 +214,7 @@ int main() test_matrix3x2_get_scale(); test_matrix3x2_get_translate(); test_matrix3x2_transform(); + test_matrix3x2_inverse(); return ::boost::report_errors(); } From 853bc1266b3d287bafbb571e12d00e01df6a8734 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Wed, 20 Jan 2021 23:11:26 +0100 Subject: [PATCH 009/193] Fix typo in copyright headers (#524) --- test/extension/toolbox/Jamfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extension/toolbox/Jamfile b/test/extension/toolbox/Jamfile index a68af63f38..a2a78f7bed 100644 --- a/test/extension/toolbox/Jamfile +++ b/test/extension/toolbox/Jamfile @@ -1,7 +1,7 @@ # Boost.GIL (Generic Image Library) - Toolbox tests # # Copyright (c) 2012 Christian Henning -# Copyright (c) 2012-202 Mateusz Loskot +# Copyright (c) 2012-2020 Mateusz Loskot # # Distributed under 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) From eef475e27a91df8c912fbf82f330d1af955743ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Thu, 21 Jan 2021 17:19:39 +0100 Subject: [PATCH 010/193] ci: Temporarily set B2_VERBOSE=0 for CircleCI Helping to investigate https://github.com/boostorg/build/issues/704 --- .circleci/config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5779c9a34c..b56aaf123e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -87,6 +87,7 @@ env_gcc_cpp11_dbg: &env_gcc_cpp11_dbg - OPTIMIZ: "" - CXXSTD: "11" - LNKFLAG: "" + - B2_VERBOSE: "0" env_gcc_cpp11_opt_speed: &env_gcc_cpp11_opt_speed environment: @@ -95,6 +96,7 @@ env_gcc_cpp11_opt_speed: &env_gcc_cpp11_opt_speed - OPTIMIZ: "optimization=speed" - CXXSTD: "11" - LNKFLAG: "" + - B2_VERBOSE: "0" env_clang_cpp11_dbg: &env_clang_cpp11_dbg environment: @@ -103,6 +105,7 @@ env_clang_cpp11_dbg: &env_clang_cpp11_dbg - OPTIMIZ: "" - CXXSTD: "11" - LNKFLAG: "" + - B2_VERBOSE: "0" env_clang_cpp11_opt_speed: &env_clang_cpp11_opt_speed environment: @@ -111,6 +114,7 @@ env_clang_cpp11_opt_speed: &env_clang_cpp11_opt_speed - OPTIMIZ: "optimization=speed" - CXXSTD: "11" - LNKFLAG: "" + - B2_VERBOSE: "0" ############################################################################## # Build configurations From 470923be362881f5ea9bd72414a95690f8cceb85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Fri, 22 Jan 2021 18:01:54 +0100 Subject: [PATCH 011/193] Fix default ctor of homogeneous_color_base for reference pixel elements (#542) If `Element` is a reference, then Element v{} is ill-formed. Refines #273 which aimed to correctly value-initialize channel and pixel value members --- include/boost/gil/color_base.hpp | 77 +++++++++++++++++++++++--------- test/core/pixel/test_fixture.hpp | 2 +- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/include/boost/gil/color_base.hpp b/include/boost/gil/color_base.hpp index 16609429a0..a05b409de8 100644 --- a/include/boost/gil/color_base.hpp +++ b/include/boost/gil/color_base.hpp @@ -79,8 +79,14 @@ struct homogeneous_color_base { using layout_t = Layout; - homogeneous_color_base() = default; - homogeneous_color_base(Element v) : v0_(v) {} + template + < + typename U = Element, + typename = typename std::enable_if::value>::type + > + homogeneous_color_base() : v0_{} {}; + + explicit homogeneous_color_base(Element v) : v0_(v) {} template homogeneous_color_base(homogeneous_color_base const& c) @@ -100,7 +106,7 @@ struct homogeneous_color_base operator Element() const { return v0_; } private: - Element v0_{}; + Element v0_; }; /// \brief A homogeneous color base holding two color elements @@ -111,8 +117,15 @@ struct homogeneous_color_base { using layout_t = Layout; - homogeneous_color_base() = default; + template + < + typename U = Element, + typename = typename std::enable_if::value>::type + > + homogeneous_color_base() : v0_{}, v1_{} {} + explicit homogeneous_color_base(Element v) : v0_(v), v1_(v) {} + homogeneous_color_base(Element v0, Element v1) : v0_(v0), v1_(v1) {} template @@ -174,8 +187,8 @@ struct homogeneous_color_base } private: - Element v0_{}; - Element v1_{}; + Element v0_; + Element v1_; }; /// \brief A homogeneous color base holding three color elements. @@ -186,8 +199,15 @@ struct homogeneous_color_base { using layout_t = Layout; - homogeneous_color_base() = default; + template + < + typename U = Element, + typename = typename std::enable_if::value>::type + > + homogeneous_color_base() : v0_{}, v1_{}, v2_{} {} + explicit homogeneous_color_base(Element v) : v0_(v), v1_(v), v2_(v) {} + homogeneous_color_base(Element v0, Element v1, Element v2) : v0_(v0), v1_(v1), v2_(v2) {} @@ -268,9 +288,9 @@ struct homogeneous_color_base } private: - Element v0_{}; - Element v1_{}; - Element v2_{}; + Element v0_; + Element v1_; + Element v2_; }; /// \brief A homogeneous color base holding four color elements. @@ -281,8 +301,15 @@ struct homogeneous_color_base { using layout_t = Layout; - homogeneous_color_base() = default; + template + < + typename U = Element, + typename = typename std::enable_if::value>::type + > + homogeneous_color_base() : v0_{}, v1_{}, v2_{}, v3_{} {} + explicit homogeneous_color_base(Element v) : v0_(v), v1_(v), v2_(v), v3_(v) {} + homogeneous_color_base(Element v0, Element v1, Element v2, Element v3) : v0_(v0), v1_(v1), v2_(v2), v3_(v3) {} @@ -377,10 +404,10 @@ struct homogeneous_color_base } private: - Element v0_{}; - Element v1_{}; - Element v2_{}; - Element v3_{}; + Element v0_; + Element v1_; + Element v2_; + Element v3_; }; /// \brief A homogeneous color base holding five color elements. @@ -391,7 +418,15 @@ struct homogeneous_color_base { using layout_t = Layout; - homogeneous_color_base() = default; + template + < + typename U = Element, + typename = typename std::enable_if::value>::type + > + homogeneous_color_base() + : v0_{}, v1_{}, v2_{}, v3_{}, v4_{} + {} + explicit homogeneous_color_base(Element v) : v0_(v), v1_(v), v2_(v), v3_(v), v4_(v) {} @@ -505,11 +540,11 @@ struct homogeneous_color_base } private: - Element v0_{}; - Element v1_{}; - Element v2_{}; - Element v3_{}; - Element v4_{}; + Element v0_; + Element v1_; + Element v2_; + Element v3_; + Element v4_; }; #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) diff --git a/test/core/pixel/test_fixture.hpp b/test/core/pixel/test_fixture.hpp index bf4e1ed8dc..c9ef03f32a 100644 --- a/test/core/pixel/test_fixture.hpp +++ b/test/core/pixel/test_fixture.hpp @@ -69,7 +69,7 @@ class pixel_value public: using type = Pixel; using pixel_t = type; - type pixel_{}; + type pixel_; pixel_value() = default; explicit pixel_value(pixel_t const& pixel) From cb5bc9d8c233374736118290f45ac08172fa835c Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Sat, 23 Jan 2021 04:55:17 +0600 Subject: [PATCH 012/193] =?UTF-8?q?Add=20Perona=E2=80=93Malik=20anisotropi?= =?UTF-8?q?c=20diffusion=20algorithm=20(#500)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The output type must be floating point, thus a check was added to make sure it is the case. Though I had to add specific cases for float32_t as std::is_floating_point does not consider it a floating point type The accumulate part was wrong, it multiplied by delta_t on every sum, which is wrong Use 8 way nabla compute. This is just different discretization of Laplace operator https://en.wikipedia.org/wiki/Discrete_Laplace_operator Laplace stencils are now the same as in mathematical notation The new function will provide a uniform way to generate stencils by making sure directions are indexed properly Add only required stencil points: The 5 points Laplace stencil is now adding only required points and not assuming that others are zero --- example/Jamfile | 1 + example/anisotropic_diffusion.cpp | 128 ++++++ .../boost/gil/image_processing/diffusion.hpp | 421 ++++++++++++++++++ test/core/image_processing/CMakeLists.txt | 3 +- test/core/image_processing/Jamfile | 1 + .../anisotropic_diffusion.cpp | 304 +++++++++++++ test/core/test_fixture.hpp | 27 +- 7 files changed, 876 insertions(+), 9 deletions(-) create mode 100644 example/anisotropic_diffusion.cpp create mode 100644 include/boost/gil/image_processing/diffusion.hpp create mode 100644 test/core/image_processing/anisotropic_diffusion.cpp diff --git a/example/Jamfile b/example/Jamfile index fc33ce319c..02bb33163c 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -21,6 +21,7 @@ project local sources = adaptive_threshold.cpp affine.cpp + anisotropic_diffusion.cpp convolution.cpp convolve2d.cpp dynamic_image.cpp diff --git a/example/anisotropic_diffusion.cpp b/example/anisotropic_diffusion.cpp new file mode 100644 index 0000000000..03defb7566 --- /dev/null +++ b/example/anisotropic_diffusion.cpp @@ -0,0 +1,128 @@ +// +// Copyright 2020 Olzhas Zhumabek +// +// 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 +#include +#include + +#include +#include +#include +#include + +namespace gil = boost::gil; + +void gray_version(std::string const& input_path, std::string const& output_path, + unsigned int iteration_count, float kappa) +{ + gil::gray8_image_t input; + gil::read_image(input_path, input, gil::png_tag{}); + auto input_view = gil::view(input); + + gil::gray32f_image_t gray(input.dimensions()); + auto gray_view = gil::view(gray); + + gil::transform_pixels(input_view, gray_view, [](const gil::gray8_pixel_t& p) { return p[0]; }); + double sum_before = 0; + gil::for_each_pixel(gray_view, [&sum_before](gil::gray32f_pixel_t p) { sum_before += p[0]; }); + gil::gray32f_image_t output(gray.dimensions()); + auto output_view = gil::view(output); + + // gil::anisotropic_diffusion(gray_view, output_view, iteration_count, {kappa, delta_t}); + gil::default_anisotropic_diffusion(gray_view, output_view, iteration_count, kappa); + double sum_after = 0; + gil::for_each_pixel(output_view, [&sum_after](gil::gray32f_pixel_t p) { sum_after += p[0]; }); + + gil::gray8_image_t true_output(output.dimensions()); + gil::transform_pixels(output_view, gil::view(true_output), + [](gil::gray32f_pixel_t p) { return static_cast(p[0]); }); + + gil::write_view(output_path, gil::view(true_output), gil::png_tag{}); + + std::cout << "sum of intensity before diffusion: " << sum_before << '\n' + << "sum of intensity after diffusion: " << sum_after << '\n' + << "difference: " << sum_after - sum_before << '\n'; +} + +void rgb_version(const std::string& input_path, const std::string& output_path, + unsigned int iteration_count, float kappa) +{ + gil::rgb8_image_t input; + gil::read_image(input_path, input, gil::png_tag{}); + auto input_view = gil::view(input); + + gil::rgb32f_image_t gray(input.dimensions()); + auto gray_view = gil::view(gray); + + gil::transform_pixels(input_view, gray_view, [](const gil::rgb8_pixel_t& p) { + return gil::rgb32f_pixel_t(p[0], p[1], p[2]); + }); + double sum_before[3] = {}; + gil::for_each_pixel(gray_view, [&sum_before](gil::rgb32f_pixel_t p) { + sum_before[0] += p[0]; + sum_before[1] += p[1]; + sum_before[2] += p[2]; + }); + gil::rgb32f_image_t output(gray.dimensions()); + auto output_view = gil::view(output); + + // gil::anisotropic_diffusion(gray_view, output_view, iteration_count, {kappa, delta_t}); + gil::default_anisotropic_diffusion(gray_view, output_view, iteration_count, kappa); + double sum_after[3] = {}; + gil::for_each_pixel(output_view, [&sum_after](gil::rgb32f_pixel_t p) { + sum_after[0] += p[0]; + sum_after[1] += p[1]; + sum_after[2] += p[2]; + }); + + gil::rgb8_image_t true_output(output.dimensions()); + gil::transform_pixels(output_view, gil::view(true_output), [](gil::rgb32f_pixel_t p) { + return gil::rgb8_pixel_t(static_cast(p[0]), static_cast(p[1]), + static_cast(p[2])); + }); + + gil::write_view(output_path, gil::view(true_output), gil::png_tag{}); + std::cout << "sum of intensity before diffusion: (" << sum_before[0] << ", " << sum_before[1] + << ", " << sum_before[2] << ")\n" + << "sum of intensity after diffusion: (" << sum_after[0] << ", " << sum_after[1] + << ", " << sum_after[2] << ")\n" + << "difference: (" << sum_after[0] - sum_before[0] << ", " + << sum_after[1] - sum_before[1] << ", " << sum_after[2] - sum_before[2] << ")\n"; +} + +int main(int argc, char* argv[]) +{ + if (argc != 6) + { + std::cerr << "usage: " << argv[0] + << " " + " \n"; + return -1; + } + std::string input_path = argv[1]; + std::string output_path = argv[2]; + std::string colorspace = argv[3]; + + unsigned int iteration_count = static_cast(std::stoul(argv[4])); + float kappa = std::stof(argv[5]); + if (colorspace == "gray") + { + gray_version(input_path, output_path, iteration_count, kappa); + } + else if (colorspace == "rgb") + { + rgb_version(input_path, output_path, iteration_count, kappa); + } + else + { + std::cerr << "unknown colorspace option passed (did you type gray with A?)\n"; + } +} diff --git a/include/boost/gil/image_processing/diffusion.hpp b/include/boost/gil/image_processing/diffusion.hpp new file mode 100644 index 0000000000..72f55a2ae4 --- /dev/null +++ b/include/boost/gil/image_processing/diffusion.hpp @@ -0,0 +1,421 @@ +// +// Copyright 2020 Olzhas Zhumabek +// +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#include "boost/gil/detail/math.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace gil { +namespace conductivity { +struct perona_malik_conductivity +{ + double kappa; + template + Pixel operator()(Pixel input) + { + using channel_type = typename channel_type::type; + // C++11 doesn't seem to capture members + static_transform(input, input, [this](channel_type value) { + value /= kappa; + return std::exp(-std::abs(value)); + }); + + return input; + } +}; + +struct gaussian_conductivity +{ + double kappa; + template + Pixel operator()(Pixel input) + { + using channel_type = typename channel_type::type; + // C++11 doesn't seem to capture members + static_transform(input, input, [this](channel_type value) { + value /= kappa; + return std::exp(-value * value); + }); + + return input; + } +}; + +struct wide_regions_conductivity +{ + double kappa; + template + Pixel operator()(Pixel input) + { + using channel_type = typename channel_type::type; + // C++11 doesn't seem to capture members + static_transform(input, input, [this](channel_type value) { + value /= kappa; + return 1.0 / (1.0 + value * value); + }); + + return input; + } +}; + +struct more_wide_regions_conductivity +{ + double kappa; + template + Pixel operator()(Pixel input) + { + using channel_type = typename channel_type::type; + // C++11 doesn't seem to capture members + static_transform(input, input, [this](channel_type value) { + value /= kappa; + return 1.0 / std::sqrt((1.0 + value * value)); + }); + + return input; + } +}; +} // namespace diffusion + +/** + \brief contains discrete approximations of 2D Laplacian operator +*/ +namespace laplace_function { +// The functions assume clockwise enumeration of stencil points, as such +// NW North NE 0 1 2 (-1, -1) (0, -1) (+1, -1) +// West East ===> 7 3 ===> (-1, 0) (+1, 0) +// SW South SE 6 5 4 (-1, +1) (0, +1) (+1, +1) + +/** + \brief This function makes sure all Laplace functions enumerate + values in the same order and direction. + + The first element is difference North West direction, second in North, + and so on in clockwise manner. Leave element as zero if it is not + to be computed. +*/ +std::array get_directed_offsets() +{ + return {point_t{-1, -1}, point_t{0, -1}, point_t{+1, -1}, point_t{+1, 0}, + point_t{+1, +1}, point_t{0, +1}, point_t{-1, +1}, point_t{-1, 0}}; +} + +template +using stencil_type = std::array; + +/** + \brief 5 point stencil approximation of Laplacian + + Only main 4 directions are non-zero, the rest are zero +*/ +struct stencil_5points +{ + double delta_t = 0.25; + + template + stencil_type compute_laplace(SubImageView view, + point_t origin) + { + auto current = view(origin); + stencil_type stencil; + using channel_type = typename channel_type::type; + std::array offsets(get_directed_offsets()); + typename SubImageView::value_type zero_pixel; + static_fill(zero_pixel, 0); + for (std::size_t index = 0; index < offsets.size(); ++index) + { + if (index % 2 != 0) + { + static_transform(view(origin.x + offsets[index].x, origin.y + offsets[index].y), + current, stencil[index], std::minus{}); + } + else + { + stencil[index] = zero_pixel; + } + } + return stencil; + } + + template + Pixel reduce(const stencil_type& stencil) + { + auto first = stencil.begin(); + auto last = stencil.end(); + using channel_type = typename channel_type::type; + auto result = []() { + Pixel zero_pixel; + static_fill(zero_pixel, channel_type(0)); + return zero_pixel; + }(); + + for (std::size_t index : {1u, 3u, 5u, 7u}) + { + static_transform(result, stencil[index], result, std::plus{}); + } + Pixel delta_t_pixel; + static_fill(delta_t_pixel, delta_t); + static_transform(result, delta_t_pixel, result, std::multiplies{}); + + return result; + } +}; + +/** + \brief 9 point stencil approximation of Laplacian + + This is full 8 way approximation, though diagonal + elements are halved during reduction. +*/ +struct stencil_9points_standard +{ + double delta_t = 0.125; + + template + stencil_type compute_laplace(SubImageView view, + point_t origin) + { + stencil_type stencil; + auto out = stencil.begin(); + auto current = view(origin); + using channel_type = typename channel_type::type; + std::array offsets(get_directed_offsets()); + for (auto offset : offsets) + { + static_transform(view(origin.x + offset.x, origin.y + offset.y), current, *out++, + std::minus{}); + } + + return stencil; + } + + template + Pixel reduce(const stencil_type& stencil) + { + using channel_type = typename channel_type::type; + auto result = []() { + Pixel zero_pixel; + static_fill(zero_pixel, channel_type(0)); + return zero_pixel; + }(); + for (std::size_t index : {1u, 3u, 5u, 7u}) + { + static_transform(result, stencil[index], result, std::plus{}); + } + + for (std::size_t index : {0u, 2u, 4u, 6u}) + { + Pixel half_pixel; + static_fill(half_pixel, channel_type(1 / 2.0)); + static_transform(stencil[index], half_pixel, half_pixel, + std::multiplies{}); + static_transform(result, half_pixel, result, std::plus{}); + } + + Pixel delta_t_pixel; + static_fill(delta_t_pixel, delta_t); + static_transform(result, delta_t_pixel, result, std::multiplies{}); + + return result; + } +}; +} // namespace laplace_function + +namespace brightness_function { +using laplace_function::stencil_type; +struct identity +{ + template + stencil_type operator()(const stencil_type& stencil) + { + return stencil; + } +}; + +// TODO: Figure out how to implement color gradient brightness, as it +// seems to need dx and dy using sobel or scharr kernels + +struct rgb_luminance +{ + using pixel_type = rgb32f_pixel_t; + stencil_type operator()(const stencil_type& stencil) + { + stencil_type output; + std::transform(stencil.begin(), stencil.end(), output.begin(), [](const pixel_type& pixel) { + float32_t luminance = 0.2126f * pixel[0] + 0.7152f * pixel[1] + 0.0722f * pixel[2]; + pixel_type result_pixel; + static_fill(result_pixel, luminance); + return result_pixel; + }); + return output; + } +}; + +} // namespace brightness_function + +enum class matlab_connectivity +{ + minimal, + maximal +}; + +enum class matlab_conduction_method +{ + exponential, + quadratic +}; + +template +void classic_anisotropic_diffusion(const InputView& input, const OutputView& output, + unsigned int num_iter, double kappa) +{ + anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{}, + brightness_function::identity{}, + conductivity::perona_malik_conductivity{kappa}); +} + +template +void matlab_anisotropic_diffusion(const InputView& input, const OutputView& output, + unsigned int num_iter, double kappa, + matlab_connectivity connectivity, + matlab_conduction_method conduction_method) +{ + if (connectivity == matlab_connectivity::minimal) + { + if (conduction_method == matlab_conduction_method::exponential) + { + anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{}, + brightness_function::identity{}, + conductivity::gaussian_conductivity{kappa}); + } + else if (conduction_method == matlab_conduction_method::quadratic) + { + anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{}, + brightness_function::identity{}, + conductivity::gaussian_conductivity{kappa}); + } + else + { + throw std::logic_error("unhandled conduction method found"); + } + } + else if (connectivity == matlab_connectivity::maximal) + { + if (conduction_method == matlab_conduction_method::exponential) + { + anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{}, + brightness_function::identity{}, + conductivity::gaussian_conductivity{kappa}); + } + else if (conduction_method == matlab_conduction_method::quadratic) + { + anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{}, + brightness_function::identity{}, + conductivity::gaussian_conductivity{kappa}); + } + else + { + throw std::logic_error("unhandled conduction method found"); + } + } + else + { + throw std::logic_error("unhandled connectivity found"); + } +} + +template +void default_anisotropic_diffusion(const InputView& input, const OutputView& output, + unsigned int num_iter, double kappa) +{ + anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_9points_standard{}, + brightness_function::identity{}, conductivity::gaussian_conductivity{kappa}); +} + +/// \brief Performs diffusion according to Perona-Malik equation +/// +/// WARNING: Output channel type must be floating point, +/// otherwise there will be loss in accuracy which most +/// probably will lead to incorrect results (input will be unchanged). +/// Anisotropic diffusion is a smoothing algorithm that respects +/// edge boundaries and can work as an edge detector if suitable +/// iteration count is set and grayscale image view is used +/// as an input +template +void anisotropic_diffusion(const InputView& input, const OutputView& output, unsigned int num_iter, + LaplaceStrategy laplace, BrightnessFunction brightness, + DiffusivityFunction diffusivity) +{ + using input_pixel_type = typename InputView::value_type; + using pixel_type = typename OutputView::value_type; + using channel_type = typename channel_type::type; + using computation_image = image; + const auto width = input.width(); + const auto height = input.height(); + const point_t dims(width, height); + const auto zero_pixel = []() { + pixel_type pixel; + static_fill(pixel, static_cast(0)); + + return pixel; + }(); + computation_image result_image(width + 2, height + 2, zero_pixel); + auto result = view(result_image); + computation_image scratch_result_image(width + 2, height + 2, zero_pixel); + auto scratch_result = view(scratch_result_image); + transform_pixels(input, subimage_view(result, 1, 1, width, height), + [](const input_pixel_type& pixel) { + pixel_type converted; + for (std::size_t i = 0; i < num_channels{}; ++i) + { + converted[i] = pixel[i]; + } + return converted; + }); + + for (unsigned int iteration = 0; iteration < num_iter; ++iteration) + { + for (std::ptrdiff_t relative_y = 0; relative_y < height; ++relative_y) + { + for (std::ptrdiff_t relative_x = 0; relative_x < width; ++relative_x) + { + auto x = relative_x + 1; + auto y = relative_y + 1; + auto stencil = laplace.compute_laplace(result, point_t(x, y)); + auto brightness_stencil = brightness(stencil); + laplace_function::stencil_type diffusivity_stencil; + std::transform(brightness_stencil.begin(), brightness_stencil.end(), + diffusivity_stencil.begin(), diffusivity); + laplace_function::stencil_type product_stencil; + std::transform(stencil.begin(), stencil.end(), diffusivity_stencil.begin(), + product_stencil.begin(), [](pixel_type lhs, pixel_type rhs) { + static_transform(lhs, rhs, lhs, std::multiplies{}); + return lhs; + }); + static_transform(result(x, y), laplace.reduce(product_stencil), + scratch_result(x, y), std::plus{}); + } + } + using std::swap; + swap(result, scratch_result); + } + + copy_pixels(subimage_view(result, 1, 1, width, height), output); +} + +}} // namespace boost::gil diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index 5ecd590629..2ad32f4f28 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -34,7 +34,8 @@ foreach(_name hessian box_filter median_filter - sobel_scharr) + sobel_scharr + anisotropic_diffusion) 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 d9593f0793..be4c755c02 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -19,3 +19,4 @@ run hessian.cpp ; run sobel_scharr.cpp ; run box_filter.cpp ; run median_filter.cpp ; +run anisotropic_diffusion.cpp ; diff --git a/test/core/image_processing/anisotropic_diffusion.cpp b/test/core/image_processing/anisotropic_diffusion.cpp new file mode 100644 index 0000000000..58a5c9d6ce --- /dev/null +++ b/test/core/image_processing/anisotropic_diffusion.cpp @@ -0,0 +1,304 @@ +// +// Copyright 2020 Olzhas Zhumabek +// +// 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 "../test_fixture.hpp" +#include "boost/gil/algorithm.hpp" +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace gil = boost::gil; + +template +void diffusion_function_check(DiffusionFunction diffusion) +{ + using limits = std::numeric_limits; + using channel_type = typename gil::channel_type::type; + for (channel_type value = limits::min(); value <= limits::max(); ++value) + { + PixelType pixel; + gil::static_fill(pixel, value); + auto diffusivity_value = diffusion(pixel); + gil::static_for_each(diffusivity_value, [](channel_type channel_value) { + BOOST_TEST(0 <= channel_value && channel_value <= 1.0); + }); + } +} + +void brightness_function_test() +{ + std::vector rgb_pixels{ + gil::rgb32f_pixel_t(0, 11, 14), gil::rgb32f_pixel_t(2, 117, 200), + gil::rgb32f_pixel_t(223, 2, 180), gil::rgb32f_pixel_t(250, 254, 100), + gil::rgb32f_pixel_t(255, 255, 255), + }; + for (const auto& pixel : rgb_pixels) + { + boost::gil::laplace_function::stencil_type stencil; + std::fill(stencil.begin(), stencil.end(), pixel); + auto brightness_stencil = boost::gil::brightness_function::identity{}(stencil); + for (const auto& brightness_pixel : brightness_stencil) + { + BOOST_TEST(pixel == brightness_pixel); + } + } + + std::vector corresponding_luminance_values{8.878f, 98.5436f, 61.8362f, 242.0308f, 255}; + std::size_t index = 0; + for (const auto& pixel : rgb_pixels) + { + boost::gil::laplace_function::stencil_type stencil; + std::fill(stencil.begin(), stencil.end(), pixel); + auto brightness_stencil = boost::gil::brightness_function::rgb_luminance{}(stencil); + for (const auto& brightness_pixel : brightness_stencil) + { + gil::static_for_each(brightness_pixel, [&corresponding_luminance_values, + index](boost::gil::float32_t value) { + BOOST_TEST(std::abs(value - corresponding_luminance_values[index]) < 1.0f); + }); + } + ++index; + } + + std::vector gray_pixels{ + gil::gray32f_pixel_t(11), gil::gray32f_pixel_t(0), gil::gray32f_pixel_t(255), + gil::gray32f_pixel_t(123), gil::gray32f_pixel_t(17), + }; + for (const auto& pixel : gray_pixels) + { + boost::gil::laplace_function::stencil_type stencil; + std::fill(stencil.begin(), stencil.end(), pixel); + auto brightness_stencil = boost::gil::brightness_function::identity{}(stencil); + for (const auto& brightness_pixel : brightness_stencil) + { + BOOST_TEST(pixel == brightness_pixel); + } + } +} + +template +void heat_conservation_test(std::uint32_t seed) +{ + gil::test::fixture::random_value dist(seed, 0, + std::numeric_limits::max()); + + ImageType image(32, 32); + auto view = gil::view(image); + constexpr std::ptrdiff_t num_channels = gil::num_channels::value; + double before_diffusion[num_channels] = {0}; + for (auto& pixel : view) + { + for (std::size_t channel_index = 0; channel_index < num_channels; ++channel_index) + { + pixel[channel_index] = static_cast(dist()); + before_diffusion[channel_index] += pixel[channel_index]; + } + } + + OutputImageType output(32, 32); + auto output_view = gil::view(output); + gil::default_anisotropic_diffusion(view, output_view, 10, 5); + double after_diffusion[num_channels] = {0}; + for (const auto& pixel : output_view) + { + for (std::size_t channel_index = 0; channel_index < num_channels; ++channel_index) + { + after_diffusion[channel_index] += pixel[channel_index]; + } + } + + for (std::ptrdiff_t channel_index = 0; channel_index < num_channels; ++channel_index) + { + const auto percentage = + before_diffusion[channel_index] != 0 + ? std::abs(after_diffusion[channel_index] - before_diffusion[channel_index]) / + after_diffusion[channel_index] * 100.0 + : std::abs(after_diffusion[channel_index] - before_diffusion[channel_index]) / + after_diffusion[channel_index] * 100.0; +#ifdef BOOST_GIL_TEST_DEBUG + std::cout << percentage << ' '; +#endif + BOOST_TEST(percentage < 1); + } +#ifdef BOOST_GIL_TEST_DEBUG + std::cout << '\n'; +#endif +} + +template +void test_stencil_pixels(const gil::laplace_function::stencil_type& stencil, + const PixelType& reference) +{ + for (const auto& stencil_entry : stencil) + { + BOOST_TEST(stencil_entry == reference); + } +} + +template +void test_stencil(const gil::laplace_function::stencil_type& stencil, + const std::array& expected_values) +{ + for (std::size_t i = 0; i < 8; ++i) + { + PixelType expected_pixel; + gil::static_fill(expected_pixel, expected_values[i]); + BOOST_TEST(stencil[i] == expected_pixel); + } +} + +void laplace_functions_test() +{ + // sanity checks + auto zero_rgb_pixel = []() { + gil::rgb32f_pixel_t zero_pixel; + gil::static_fill(zero_pixel, 0); + return zero_pixel; + }(); + auto zero_gray_pixel = []() { + gil::gray32f_pixel_t zero_pixel; + gil::static_fill(zero_pixel, 0); + return zero_pixel; + }(); + + auto image_size = gil::point_t(16, 16); + gil::rgb32f_image_t rgb_image(image_size, zero_rgb_pixel); + gil::gray32f_image_t gray_image(image_size, zero_gray_pixel); + auto rgb = gil::view(rgb_image); + auto gray = gil::view(gray_image); + + auto s4way = gil::laplace_function::stencil_5points{}; + auto s8way = gil::laplace_function::stencil_9points_standard{}; + for (std::ptrdiff_t y = 1; y < image_size.y - 1; ++y) + { + for (std::ptrdiff_t x = 1; x < image_size.x - 1; ++x) + { + auto rgb_4way_stencil = s4way.compute_laplace(rgb, {x, y}); + auto gray_4way_stencil = s4way.compute_laplace(gray, {x, y}); + + auto rgb_8way_stencil = s8way.compute_laplace(rgb, {x, y}); + auto gray_8way_stencil = s8way.compute_laplace(gray, {x, y}); + + test_stencil_pixels(rgb_4way_stencil, zero_rgb_pixel); + test_stencil_pixels(rgb_8way_stencil, zero_rgb_pixel); + test_stencil_pixels(gray_4way_stencil, zero_gray_pixel); + test_stencil_pixels(gray_8way_stencil, zero_gray_pixel); + } + } + + // a predefined case + // 5 11 2 + // 17 25 58 + // 90 31 40 + + using rgb_pixel = gil::rgb32f_pixel_t; + rgb(1, 1) = rgb_pixel(5, 5, 5); + rgb(2, 1) = rgb_pixel(11, 11, 11); + rgb(3, 1) = rgb_pixel(2, 2, 2); + rgb(1, 2) = rgb_pixel(17, 17, 17); + rgb(2, 2) = rgb_pixel(25, 25, 25); + rgb(3, 2) = rgb_pixel(58, 58, 58); + rgb(1, 3) = rgb_pixel(90, 90, 90); + rgb(2, 3) = rgb_pixel(31, 31, 31); + rgb(3, 3) = rgb_pixel(40, 40, 40); + + using gray_pixel = gil::gray32f_pixel_t; + gray(1, 1) = gray_pixel(5); + gray(2, 1) = gray_pixel(11); + gray(3, 1) = gray_pixel(2); + gray(1, 2) = gray_pixel(17); + gray(2, 2) = gray_pixel(25); + gray(3, 2) = gray_pixel(58); + gray(1, 3) = gray_pixel(90); + gray(2, 3) = gray_pixel(31); + gray(3, 3) = gray_pixel(40); + + // 4 way stencil should be + // 0 -14 0 + // -8 33 + // 0 6 0 + + auto test_point = gil::point_t(2, 2); + std::array _4way_expected{0, -14, 0, 33, 0, 6, 0, -8}; + auto rgb_4way = s4way.compute_laplace(rgb, test_point); + test_stencil(rgb_4way, _4way_expected); + auto gray_4way = s4way.compute_laplace(gray, test_point); + test_stencil(gray_4way, _4way_expected); + + // 8 way stencil should be + // -20 -14 -23 + // -8 33 + // 65 6 15 + + std::array _8way_expected{-20, -14, -23, 33, 15, 6, 65, -8}; + auto rgb_8way = s8way.compute_laplace(rgb, test_point); + test_stencil(rgb_8way, _8way_expected); + auto gray_8way = s8way.compute_laplace(gray, test_point); + test_stencil(gray_8way, _8way_expected); + + // reduce result for 4 way should be (-14 - 8 + 6 + 33) * 0.25 = 4.25 + auto rgb_reduced_4way = s4way.reduce(rgb_4way); + gil::static_for_each(rgb_reduced_4way, + [](gil::float32_t value) { BOOST_TEST(value == 4.25f); }); + auto gray_reduced_4way = s4way.reduce(gray_4way); + gil::static_for_each(gray_reduced_4way, + [](gil::float32_t value) { BOOST_TEST(value == 4.25f); }); + + // reduce result for 8 way should be ((-20-23+15+65)*0.5+(-14+33+6-8)) * 0.125 = (18.5+17) * + // 0.125 = 4.4375 + auto rgb_reduced_8way = s8way.reduce(rgb_8way); + gil::static_for_each(rgb_reduced_8way, + [](gil::float32_t value) { BOOST_TEST(value == 4.4375); }); + auto gray_reduced_8way = s8way.reduce(gray_8way); + gil::static_for_each(gray_reduced_8way, + [](gil::float32_t value) { BOOST_TEST(value == 4.4375); }); +} + +int main() +{ + for (std::uint32_t seed = 0; seed < 100; ++seed) + { + heat_conservation_test(seed); + heat_conservation_test(seed); + } + + for (double kappa = 5; kappa <= 70; ++kappa) + { + diffusion_function_check( + gil::conductivity::perona_malik_conductivity{kappa}); + diffusion_function_check( + gil::conductivity::gaussian_conductivity{kappa}); + diffusion_function_check( + gil::conductivity::wide_regions_conductivity{kappa}); + diffusion_function_check( + gil::conductivity::more_wide_regions_conductivity{kappa}); + + diffusion_function_check( + gil::conductivity::perona_malik_conductivity{kappa}); + diffusion_function_check( + gil::conductivity::gaussian_conductivity{kappa}); + diffusion_function_check( + gil::conductivity::wide_regions_conductivity{kappa}); + diffusion_function_check( + gil::conductivity::more_wide_regions_conductivity{kappa}); + } + + brightness_function_test(); + + laplace_functions_test(); + + return boost::report_errors(); +} diff --git a/test/core/test_fixture.hpp b/test/core/test_fixture.hpp index 819b2cac22..d7a35abe9a 100644 --- a/test/core/test_fixture.hpp +++ b/test/core/test_fixture.hpp @@ -8,8 +8,8 @@ #ifndef BOOST_GIL_TEST_CORE_TEST_FIXTURE_HPP #define BOOST_GIL_TEST_CORE_TEST_FIXTURE_HPP -#include #include +#include #include #include @@ -60,19 +60,30 @@ template struct random_value { static_assert(std::is_integral::value, "T must be integral type"); - static constexpr auto range_min = std::numeric_limits::min(); - static constexpr auto range_max = std::numeric_limits::max(); - random_value() : rng_(rd_()), uid_(range_min, range_max) {} + random_value(T range_min = std::numeric_limits::min(), + T range_max = std::numeric_limits::max()) + : uid_(range_min, range_max) + {} + + random_value(std::uint32_t seed, T minimum, T maximum) : rng_(seed), uid_(minimum, maximum) + {} T operator()() { - auto value = uid_(rng_); - BOOST_ASSERT(range_min <= value && value <= range_max); - return static_cast(value); + return uid_(rng_); + } + + T range_min() const noexcept + { + return uid_.a(); + } + + T range_max() const noexcept + { + return uid_.b(); } - std::random_device rd_; std::mt19937 rng_; std::uniform_int_distribution::type> uid_; }; From b23c5e75b2999cf363a7f6af6b806c771de221b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 23 Jan 2021 14:13:07 +0100 Subject: [PATCH 013/193] doc: Display GIL is header-only in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 055555f8aa..0c7367c59a 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Documentation | AppVeyor | Azure Pipelines | Travis CI | CircleCI Boost.GIL is a part of the [Boost C++ Libraries](http://github.com/boostorg). -The Boost Generic Image Library (GIL) is a **C++11** library that abstracts image +The Boost Generic Image Library (GIL) is a **C++11** header-only library that abstracts image representations from algorithms and allows writing code that can work on a variety of images with performance similar to hand-writing for a specific image type. From 0e372a10bebd72cccbd76bb6ee2fb636c83210fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 23 Jan 2021 14:16:23 +0100 Subject: [PATCH 014/193] docs: Display GIL is header-only in README --- doc/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index 737cdaaba0..2a52bd6fa1 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,7 +1,7 @@ Boost Generic Image Library =========================== -The Generic Image Library (GIL) is a C++11 library that abstracts image +The Generic Image Library (GIL) is a C++11 header-only library that abstracts image representations from algorithms and allows writing code that can work on a variety of images with performance similar to hand-writing for a specific image type. From 3e729e5dae4c0ba6f407f6885bf6a18d525e8489 Mon Sep 17 00:00:00 2001 From: Debabrata Mandal <32168969+codejaeger@users.noreply.github.com> Date: Sun, 24 Jan 2021 04:32:51 +0530 Subject: [PATCH 015/193] Add histogram class and related functionality (#499) A new histogram class proposed with close suport for gil image constructs. Shift the stl support implmentation to extension to serve as example for overloading fill_histogram. Add cumulative histogram and histogram normalization. Co-authored-by: debabrata1 --- example/Jamfile | 1 + example/histogram.cpp | 71 +- example/tutorial_histogram.cpp | 49 ++ include/boost/gil.hpp | 1 + include/boost/gil/extension/histogram/std.hpp | 172 +++++ include/boost/gil/histogram.hpp | 717 ++++++++++++++++++ test/core/CMakeLists.txt | 1 + test/core/Jamfile | 1 + test/core/histogram/CMakeLists.txt | 37 + test/core/histogram/Jamfile | 21 + test/core/histogram/access.cpp | 35 + test/core/histogram/constructor.cpp | 31 + test/core/histogram/cumulative.cpp | 54 ++ test/core/histogram/dimension.cpp | 31 + test/core/histogram/fill.cpp | 282 +++++++ test/core/histogram/hash_tuple.cpp | 35 + test/core/histogram/helpers.cpp | 113 +++ test/core/histogram/is_compatible.cpp | 72 ++ test/core/histogram/key.cpp | 63 ++ test/core/histogram/sub_histogram.cpp | 58 ++ test/core/histogram/utilities.cpp | 214 ++++++ test/extension/CMakeLists.txt | 4 + test/extension/Jamfile | 1 + test/extension/histogram/CMakeLists.txt | 27 + test/extension/histogram/Jamfile | 11 + test/extension/histogram/histogram.cpp | 176 +++++ 26 files changed, 2244 insertions(+), 34 deletions(-) create mode 100644 example/tutorial_histogram.cpp create mode 100644 include/boost/gil/extension/histogram/std.hpp create mode 100644 include/boost/gil/histogram.hpp create mode 100644 test/core/histogram/CMakeLists.txt create mode 100644 test/core/histogram/Jamfile create mode 100644 test/core/histogram/access.cpp create mode 100644 test/core/histogram/constructor.cpp create mode 100644 test/core/histogram/cumulative.cpp create mode 100644 test/core/histogram/dimension.cpp create mode 100644 test/core/histogram/fill.cpp create mode 100644 test/core/histogram/hash_tuple.cpp create mode 100644 test/core/histogram/helpers.cpp create mode 100644 test/core/histogram/is_compatible.cpp create mode 100644 test/core/histogram/key.cpp create mode 100644 test/core/histogram/sub_histogram.cpp create mode 100644 test/core/histogram/utilities.cpp create mode 100644 test/extension/histogram/CMakeLists.txt create mode 100644 test/extension/histogram/Jamfile create mode 100644 test/extension/histogram/histogram.cpp diff --git a/example/Jamfile b/example/Jamfile index 02bb33163c..48b9e51aef 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -34,6 +34,7 @@ local sources = resize.cpp sobel_scharr.cpp threshold.cpp + tutorial_histogram.cpp x_gradient.cpp ; diff --git a/example/histogram.cpp b/example/histogram.cpp index b1b4896429..229b8d9b69 100644 --- a/example/histogram.cpp +++ b/example/histogram.cpp @@ -1,49 +1,52 @@ // -// Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2020 Debabrata Mandal // // Distributed under 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 +#include +#include -// Example file to demonstrate a way to compute histogram +#include using namespace boost::gil; -template -void gray_image_hist(GrayView const& img_view, R& hist) -{ - for (auto it = img_view.begin(); it != img_view.end(); ++it) - ++hist[*it]; - - // Alternatively, prefer the algorithm with lambda - // for_each_pixel(img_view, [&hist](gray8_pixel_t const& pixel) { - // ++hist[pixel]; - // }); -} - -template -void get_hist(const V& img_view, R& hist) { - gray_image_hist(color_converted_view(img_view), hist); -} - -int main() { - rgb8_image_t img; - read_image("test.jpg", img, jpeg_tag()); +/* +This file explains how to use the histogram class and some of its features +that can be applied for a variety of tasks. +*/ - int histogram[256]; - std::fill(histogram,histogram + 256, 0); - get_hist(const_view(img), histogram); - - std::fstream histo_file("out-histogram.txt", std::ios::out); - for(std::size_t ii = 0; ii < 256; ++ii) - histo_file << histogram[ii] << std::endl; - histo_file.close(); +int main() +{ + // Create a histogram class. Use uint or unsigned short as the default axes type in most cases. + histogram h; + + // Fill histogram with GIL images (of any color space) + gray8_image_t g; + read_image("test_adaptive.png", g, png_tag{}); + + fill_histogram + ( + view(g), // Input image view + h, // Histogram to be filled + 1, // Histogram bin widths + false, // Specify whether to accumulate over the values already present in h (default = false) + true, // Specify whether to have a sparse or continuous histogram (default = true) + false, // Specify if image mask is to be specified + {{}}, // Mask as a 2D vector. Used only if prev argument specified + {0}, // Lower limit on the values in histogram (default numeric_limit::min() on axes) + {255}, // Upper limit on the values in histogram (default numeric_limit::max() on axes) + true // Use specified limits if this is true (default is false) + ); + + // Normalize the histogram + h.normalize(); + + // Get a cumulative histogram from the histogram + auto h2 = cumulative_histogram(h); return 0; } diff --git a/example/tutorial_histogram.cpp b/example/tutorial_histogram.cpp new file mode 100644 index 0000000000..b1b4896429 --- /dev/null +++ b/example/tutorial_histogram.cpp @@ -0,0 +1,49 @@ +// +// Copyright 2005-2007 Adobe Systems Incorporated +// +// Distributed under 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 + +// Example file to demonstrate a way to compute histogram + +using namespace boost::gil; + +template +void gray_image_hist(GrayView const& img_view, R& hist) +{ + for (auto it = img_view.begin(); it != img_view.end(); ++it) + ++hist[*it]; + + // Alternatively, prefer the algorithm with lambda + // for_each_pixel(img_view, [&hist](gray8_pixel_t const& pixel) { + // ++hist[pixel]; + // }); +} + +template +void get_hist(const V& img_view, R& hist) { + gray_image_hist(color_converted_view(img_view), hist); +} + +int main() { + rgb8_image_t img; + read_image("test.jpg", img, jpeg_tag()); + + int histogram[256]; + std::fill(histogram,histogram + 256, 0); + get_hist(const_view(img), histogram); + + std::fstream histo_file("out-histogram.txt", std::ios::out); + for(std::size_t ii = 0; ii < 256; ++ii) + histo_file << histogram[ii] << std::endl; + histo_file.close(); + + return 0; +} diff --git a/include/boost/gil.hpp b/include/boost/gil.hpp index 5fc7d15819..b5db505d9b 100644 --- a/include/boost/gil.hpp +++ b/include/boost/gil.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/gil/extension/histogram/std.hpp b/include/boost/gil/extension/histogram/std.hpp new file mode 100644 index 0000000000..58dcae80a9 --- /dev/null +++ b/include/boost/gil/extension/histogram/std.hpp @@ -0,0 +1,172 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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_EXTENSION_HISTOGRAM_STL_HISTOGRAM_HPP +#define BOOST_GIL_EXTENSION_HISTOGRAM_STL_HISTOGRAM_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { namespace gil { + +////////////////////////////////////////////////////////// +/// Histogram extension for STL container +////////////////////////////////////////////////////////// +/// \defgroup Histogram - STL Containers +/// \brief Collection of functions to provide histogram support in GIL using Standard +/// Template Library Containers +/// The conversion from Boost.GIL images to compatible histograms are provided. The supported +/// container types would be std::vector, std::array, std::map. +/// +/// Some general constraints on STL extension:- +/// 1. Supports only 1D histogram. +/// 2. Cannot use signed images with compatible random access containers. +/// 3. Automatic resize of std::array in case of shortage of bins, to ensure +/// correctness comes before performance. +/// 4. Container key type (if exists) has to be one of std::integral types to be +/// GIL compatible. +/// 5. Container value type has to be of std::arithmetic types. +/// + +/// +/// \ingroup Histogram - STL Containers +/// \brief Overload for std::vector of fill_histogram +/// +template +void fill_histogram(SrcView const& srcview, std::vector& histogram, bool accumulate = false) +{ + gil_function_requires>(); + static_assert(std::is_arithmetic::value, "Improper container type for images."); + static_assert( + std::is_unsigned::type>::value, + "Improper container type for signed images."); + + using channel_t = typename channel_type::type; + using pixel_t = pixel; + + if (!accumulate) + histogram.clear(); + histogram.resize(std::numeric_limits::max() + 1); + + for_each_pixel(color_converted_view(srcview), [&](pixel_t const& p) { + ++histogram[static_cast(p)]; + }); +} + +/// \ingroup Histogram - STL Containers +/// \brief Overload for std::array of fill_histogram +/// +template +void fill_histogram(SrcView const& srcview, std::array& histogram, bool accumulate = false) +{ + gil_function_requires>(); + static_assert(std::is_arithmetic::value && N > 0, "Improper container type for images."); + static_assert( + std::is_unsigned::type>::value, + "Improper container type for signed images."); + + using channel_t = typename channel_type::type; + using pixel_t = pixel; + + const size_t pixel_max = std::numeric_limits::max(); + const float scale = (histogram.size() - 1.0f) / pixel_max; + + if (!accumulate) + std::fill(std::begin(histogram), std::end(histogram), 0); + + for_each_pixel(color_converted_view(srcview), [&](pixel_t const& p) { + ++histogram[static_cast(p * scale)]; + }); +} + +/// \ingroup Histogram - STL Containers +/// \brief Overload for std::map of fill_histogram +/// +template +void fill_histogram(SrcView const& srcview, std::map& histogram, bool accumulate = false) +{ + gil_function_requires>(); + static_assert( + std::is_arithmetic::value && std::is_integral::value, + "Improper container type for images."); + + using channel_t = typename channel_type::type; + using pixel_t = pixel; + + if (!accumulate) + histogram.clear(); + + for_each_pixel(color_converted_view(srcview), [&](pixel_t const& p) { + ++histogram[static_cast(p)]; + }); +} + +/// \ingroup Histogram - STL Containers +/// \brief Overload for std::vector of cumulative_histogram +/// +template +std::vector cumulative_histogram(std::vector& hist) +{ + std::vector cumulative_hist(hist.size()); + static_assert(std::is_arithmetic::value, "Improper container type for images."); + T cumulative_counter = 0; + for (std::size_t i = 0; i < hist.size(); i++) + { + cumulative_counter += hist[i]; + cumulative_hist[i] = cumulative_counter; + } + return cumulative_hist; +} + +/// \ingroup Histogram - STL Containers +/// \brief Overload for std::array of cumulative_histogram +/// +template +std::array cumulative_histogram(std::array& histogram) +{ + std::array cumulative_hist; + static_assert(std::is_arithmetic::value && N > 0, "Improper container type for images."); + T cumulative_counter = 0; + for (std::size_t i = 0; i < N; i++) + { + cumulative_counter += histogram[i]; + cumulative_hist[i] = cumulative_counter; + } + return cumulative_hist; +} + +/// \ingroup Histogram - STL Containers +/// \brief Overload for std::map of cumulative_histogram +/// +template +std::map cumulative_histogram(std::map& histogram) +{ + std::map cumulative_hist; + static_assert( + std::is_arithmetic::value && std::is_integral::value, + "Improper container type for images."); + T2 cumulative_counter = 0; + for (auto const& it : histogram) + { + cumulative_counter += it.second; + cumulative_hist[it.first] = cumulative_counter; + } + return cumulative_hist; +} + +}} // namespace boost::gil + +#endif diff --git a/include/boost/gil/histogram.hpp b/include/boost/gil/histogram.hpp new file mode 100644 index 0000000000..bdbf9a6d7b --- /dev/null +++ b/include/boost/gil/histogram.hpp @@ -0,0 +1,717 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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_HISTOGRAM_HPP +#define BOOST_GIL_HISTOGRAM_HPP + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace gil { + +////////////////////////////////////////////////////////// +/// Histogram +////////////////////////////////////////////////////////// +/// \defgroup Histogram Histogram +/// \brief Contains description of the boost.gil.histogram class, extensions provided in place +/// of the default class, algorithms over the histogram class (both extensions and the +/// default class) +/// + +namespace detail { + +/// \defgroup Histogram-Helpers Histogram-Helpers +/// \brief Helper implementations supporting the histogram class. + +/// \ingroup Histogram-Helpers +/// +template +inline typename std::enable_if::type + hash_tuple_impl(std::size_t&, std::tuple const&) +{ +} + +/// \ingroup Histogram-Helpers +/// +template +inline typename std::enable_if::type + hash_tuple_impl(std::size_t& seed, std::tuple const& t) +{ + boost::hash_combine(seed, std::get(t)); + hash_tuple_impl(seed, t); +} + +/// \ingroup Histogram-Helpers +/// \brief Functor provided for the hashing of tuples. +/// The following approach makes use hash_combine from +/// boost::container_hash. Although there is a direct hashing +/// available for tuples, this approach will ease adopting in +/// future to a std::hash_combine. In case std::hash extends +/// support to tuples this functor as well as the helper +/// implementation hash_tuple_impl can be removed. +/// +template +struct hash_tuple +{ + std::size_t operator()(std::tuple const& t) const + { + std::size_t seed = 0; + hash_tuple_impl<0>(seed, t); + return seed; + } +}; + +/// \ingroup Histogram-Helpers +/// \todo With C++14 and using auto we don't need the decltype anymore +/// +template +auto pixel_to_tuple(Pixel const& p, boost::mp11::index_sequence) + -> decltype(std::make_tuple(p[I]...)) +{ + return std::make_tuple(p[I]...); +} + +/// \ingroup Histogram-Helpers +/// \todo With C++14 and using auto we don't need the decltype anymore +/// +template +auto tuple_to_tuple(Tuple const& t, boost::mp11::index_sequence) + -> decltype(std::make_tuple(std::get(t)...)) +{ + return std::make_tuple(std::get(t)...); +} + +/// \ingroup Histogram-Helpers +/// +template +bool tuple_compare(Tuple const& t1, Tuple const& t2, boost::mp11::index_sequence) +{ + std::array::value> comp_list; + comp_list = {std::get(t1) <= std::get(t2)...}; + bool comp = true; + for (std::size_t i = 0; i < comp_list.size(); i++) + { + comp = comp & comp_list[i]; + } + return comp; +} + +/// \ingroup Histogram-Helpers +/// \brief Compares 2 tuples and outputs t1 <= t2 +/// Comparison is not in a lexicographic manner but on every element of the tuple hence +/// (2, 2) > (1, 3) evaluates to false +/// +template +bool tuple_compare(Tuple const& t1, Tuple const& t2) +{ + std::size_t const tuple_size = std::tuple_size::value; + auto index_list = boost::mp11::make_index_sequence{}; + return tuple_compare(t1, t2, index_list); +} + +/// \ingroup Histogram-Helpers +/// \brief Provides equivalent of std::numeric_limits for type std::tuple +/// tuple_limit gets called with only tuples having integral elements +/// +template +struct tuple_limit +{ + static constexpr Tuple min() + { + return min_impl(boost::mp11::make_index_sequence::value>{}); + } + static constexpr Tuple max() + { + return max_impl(boost::mp11::make_index_sequence::value>{}); + } + +private: + template + static constexpr Tuple min_impl(boost::mp11::index_sequence) + { + return std::make_tuple( + std::numeric_limits::type>::min()...); + } + + template + static constexpr Tuple max_impl(boost::mp11::index_sequence) + { + return std::make_tuple( + std::numeric_limits::type>::max()...); + } +}; + +/// \ingroup Histogram-Helpers +/// \brief Filler is used to fill the histogram class with all values between a specified range +/// This functor is used when sparsefill is false, since all the keys need to be present +/// in that case. +/// Currently on 1D implementation is available, extend by adding specialization for 2D +/// and higher dimensional cases. +/// +template +struct filler +{ + template + void operator()(Container&, Tuple&, Tuple&, std::size_t) + { + } +}; + +/// \ingroup Histogram-Helpers +/// \brief Specialisation for 1D histogram. +template <> +struct filler<1> +{ + template + void operator()(Container& hist, Tuple& lower, Tuple& upper, std::size_t bin_width = 1) + { + for (auto i = std::get<0>(lower); std::get<0>(upper) - i >= bin_width; i += bin_width) + { + hist(i / bin_width) = 0; + } + hist(std::get<0>(upper) / bin_width) = 0; + } +}; + +} //namespace detail + +/// +/// \class boost::gil::histogram +/// \ingroup Histogram +/// \brief Default histogram class provided by boost::gil. +/// +/// The class inherits over the std::unordered_map provided by STL. A complete example/tutorial +/// of how to use the class resides in the docs. +/// Simple calling syntax for a 3D dimensional histogram : +/// \code +/// histogram h; +/// h(1, 1, 1) = 0; +/// \endcode +/// This is just a starter to what all can be achieved with it, refer to the docs for the +/// full demo. +/// +template +class histogram : public std::unordered_map, double, detail::hash_tuple> +{ + using base_t = std::unordered_map, double, detail::hash_tuple>; + using bin_t = boost::mp11::mp_list; + using key_t = typename base_t::key_type; + using mapped_t = typename base_t::mapped_type; + using value_t = typename base_t::value_type; + +public: + histogram() = default; + + /// \brief Returns the number of dimensions(axes) the class supports. + static constexpr std::size_t dimension() + { + return std::tuple_size::value; + } + + /// \brief Returns bin value corresponding to specified tuple + mapped_t& operator()(T... indices) + { + auto key = std::make_tuple(indices...); + std::size_t const index_dimension = std::tuple_size>::value; + std::size_t const histogram_dimension = dimension(); + static_assert(histogram_dimension == index_dimension, "Dimensions do not match."); + + return base_t::operator[](key); + } + + /// \brief Checks if 2 histograms are equal. Ignores type, and checks if + /// the keys (after type casting) match. + template + bool equals(OtherType const& otherhist) const + { + bool check = (dimension() == otherhist.dimension()); + + using other_value_t = typename OtherType::value_type; + std::for_each(otherhist.begin(), otherhist.end(), [&](other_value_t const& v) { + key_t key = key_from_tuple(v.first); + if (base_t::find(key) != base_t::end()) + { + check = check & (base_t::at(key) == otherhist.at(v.first)); + } + else + { + check = false; + } + }); + return check; + } + + /// \brief Checks if the histogram class is compatible to be used with + /// a GIL image type + static constexpr bool is_pixel_compatible() + { + using bin_types = boost::mp11::mp_list; + return boost::mp11::mp_all_of::value; + } + + /// \brief Checks if the histogram class is compatible to be used with + /// the specified tuple type + template + bool is_tuple_compatible(Tuple const&) + { + std::size_t const tuple_size = std::tuple_size::value; + std::size_t const histogram_size = dimension(); + // TODO : Explore consequence of using if-constexpr + using sequence_type = typename std::conditional + < + tuple_size >= histogram_size, + boost::mp11::make_index_sequence, + boost::mp11::make_index_sequence + >::type; + + if (is_tuple_size_compatible()) + return is_tuple_type_compatible(sequence_type{}); + else + return false; + } + + /// \brief Returns a key compatible to be used as the histogram key + /// from the input tuple + template + key_t key_from_tuple(Tuple const& t) const + { + using index_list = boost::mp11::mp_list_c; + std::size_t const index_list_size = boost::mp11::mp_size::value; + std::size_t const tuple_size = std::tuple_size::value; + std::size_t const histogram_dimension = dimension(); + + static_assert( + ((index_list_size != 0 && index_list_size == histogram_dimension) || + (tuple_size == histogram_dimension)), + "Tuple and histogram key of different sizes"); + + using new_index_list = typename std::conditional + < + index_list_size == 0, + boost::mp11::mp_list_c, + index_list + >::type; + + std::size_t const min = + boost::mp11::mp_min_element::value; + + std::size_t const max = + boost::mp11::mp_max_element::value; + + static_assert((0 <= min && max < tuple_size) || index_list_size == 0, "Index out of Range"); + + using seq1 = boost::mp11::make_index_sequence; + using seq2 = boost::mp11::index_sequence; + // TODO : Explore consequence of using if-constexpr + using sequence_type = typename std::conditional::type; + + auto key = detail::tuple_to_tuple(t, sequence_type{}); + static_assert( + is_tuple_type_compatible(seq1{}), + "Tuple type and histogram type not compatible."); + + return make_histogram_key(key, seq1{}); + } + + /// \brief Returns a histogram compatible key from the input pixel which + /// can be directly used + template + key_t key_from_pixel(Pixel const& p) const + { + using index_list = boost::mp11::mp_list_c; + std::size_t const index_list_size = boost::mp11::mp_size::value; + std::size_t const pixel_dimension = num_channels::value; + std::size_t const histogram_dimension = dimension(); + + static_assert( + ((index_list_size != 0 && index_list_size == histogram_dimension) || + (index_list_size == 0 && pixel_dimension == histogram_dimension)) && + is_pixel_compatible(), + "Pixels and histogram key are not compatible."); + + using new_index_list = typename std::conditional + < + index_list_size == 0, + boost::mp11::mp_list_c, + index_list + >::type; + + std::size_t const min = + boost::mp11::mp_min_element::value; + + std::size_t const max = + boost::mp11::mp_max_element::value; + + static_assert( + (0 <= min && max < pixel_dimension) || index_list_size == 0, "Index out of Range"); + + using seq1 = boost::mp11::make_index_sequence; + using seq2 = boost::mp11::index_sequence; + using sequence_type = typename std::conditional::type; + + auto key = detail::pixel_to_tuple(p, sequence_type{}); + return make_histogram_key(key, seq1{}); + } + + /// \brief Return nearest smaller key to specified histogram key + key_t nearest_key(key_t const& k) const + { + using check_list = boost::mp11::mp_list...>; + static_assert( + boost::mp11::mp_all_of::value, + "Keys are not comparable."); + auto nearest_k = k; + if (base_t::find(k) != base_t::end()) + { + return nearest_k; + } + else + { + bool once = true; + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) { + if (v.first <= k) + { + if (once) + { + once = !once; + nearest_k = v.first; + } + else if (nearest_k < v.first) + nearest_k = v.first; + } + }); + return nearest_k; + } + } + + /// \brief Fills the histogram with the input image view + template + void fill( + SrcView const& srcview, + std::size_t bin_width = 1, + bool applymask = false, + std::vector> mask = {}, + key_t lower = key_t(), + key_t upper = key_t(), + bool setlimits = false) + { + gil_function_requires>(); + using channel_t = typename channel_type::type; + + for (std::ptrdiff_t src_y = 0; src_y < srcview.height(); ++src_y) + { + auto src_it = srcview.row_begin(src_y); + for (std::ptrdiff_t src_x = 0; src_x < srcview.width(); ++src_x) + { + if (applymask && !mask[src_y][src_x]) + continue; + auto scaled_px = src_it[src_x]; + static_for_each(scaled_px, [&](channel_t& ch) { + ch = ch / bin_width; + }); + auto key = key_from_pixel(scaled_px); + if (!setlimits || + (detail::tuple_compare(lower, key) && detail::tuple_compare(key, upper))) + base_t::operator[](key)++; + } + } + } + + /// \brief Can return a subset or a mask over the current histogram + template + histogram sub_histogram(Tuple const& t1, Tuple const& t2) + { + using index_list = boost::mp11::mp_list_c; + std::size_t const index_list_size = boost::mp11::mp_size::value; + std::size_t const histogram_dimension = dimension(); + + std::size_t const min = + boost::mp11::mp_min_element::value; + + std::size_t const max = + boost::mp11::mp_max_element::value; + + static_assert( + (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension, + "Index out of Range"); + + using seq1 = boost::mp11::make_index_sequence; + using seq2 = boost::mp11::index_sequence; + + static_assert( + is_tuple_type_compatible(seq1{}), + "Tuple type and histogram type not compatible."); + + auto low = make_histogram_key(t1, seq1{}); + auto low_key = detail::tuple_to_tuple(low, seq2{}); + auto high = make_histogram_key(t2, seq1{}); + auto high_key = detail::tuple_to_tuple(high, seq2{}); + + histogram sub_h; + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& k) { + auto tmp_key = detail::tuple_to_tuple(k.first, seq2{}); + if (low_key <= tmp_key && tmp_key <= high_key) + sub_h[k.first] += base_t::operator[](k.first); + }); + return sub_h; + } + + /// \brief Returns a sub-histogram over specified axes + template + histogram>...> sub_histogram() + { + using index_list = boost::mp11::mp_list_c; + std::size_t const index_list_size = boost::mp11::mp_size::value; + std::size_t const histogram_dimension = dimension(); + + std::size_t const min = + boost::mp11::mp_min_element::value; + + std::size_t const max = + boost::mp11::mp_max_element::value; + + static_assert( + (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension, + "Index out of Range"); + + histogram>...> sub_h; + + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) { + auto sub_key = + detail::tuple_to_tuple(v.first, boost::mp11::index_sequence{}); + sub_h[sub_key] += base_t::operator[](v.first); + }); + return sub_h; + } + + /// \brief Normalize this histogram class + void normalize() + { + double sum = 0.0; + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) { + sum += v.second; + }); + // std::cout<<(long int)sum<<"asfe"; + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) { + base_t::operator[](v.first) = v.second / sum; + }); + } + + /// \brief Return the sum count of all bins + double sum() const + { + double sum = 0.0; + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) { + sum += v.second; + }); + return sum; + } + + /// \brief Return the minimum key in histogram + key_t min_key() const + { + key_t min_key = base_t::begin()->first; + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) { + if (v.first < min_key) + min_key = v.first; + }); + return min_key; + } + + /// \brief Return the maximum key in histogram + key_t max_key() const + { + key_t max_key = base_t::begin()->first; + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) { + if (v.first > max_key) + max_key = v.first; + }); + return max_key; + } + + /// \brief Return sorted keys in a vector + std::vector sorted_keys() const + { + std::vector sorted_keys; + std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) { + sorted_keys.push_back(v.first); + }); + std::sort(sorted_keys.begin(), sorted_keys.end()); + return sorted_keys; + } + +private: + template + key_t make_histogram_key(Tuple const& t, boost::mp11::index_sequence) const + { + return std::make_tuple( + static_cast>>( + std::get(t))...); + } + + template + static constexpr bool is_tuple_type_compatible(boost::mp11::index_sequence) + { + using tp = boost::mp11::mp_list + < + typename std::is_convertible + < + boost::mp11::mp_at>, + typename std::tuple_element::type + >::type... + >; + return boost::mp11::mp_all_of::value; + } + + template + static constexpr bool is_tuple_size_compatible() + { + return (std::tuple_size::value == dimension()); + } +}; + +/// +/// \fn void fill_histogram +/// \ingroup Histogram Algorithms +/// \tparam SrcView Input image view +/// \tparam Container Input histogram container +/// \brief Overload this function to provide support for boost::gil::histogram or +/// any other external histogram +/// +/// Example : +/// \code +/// histogram h; +/// fill_histogram(view(img), h); +/// \endcode +/// +template +void fill_histogram(SrcView const&, Container&); + +/// +/// \fn void fill_histogram +/// \ingroup Histogram Algorithms +/// @param srcview Input Input image view +/// @param hist Output Histogram to be filled +/// @param bin_width Input Specify the bin widths for the histogram. +/// @param accumulate Input Specify whether to accumulate over the values already present in h (default = false) +/// @param sparsaefill Input Specify whether to have a sparse or continuous histogram (default = true) +/// @param applymask Input Specify if image mask is to be specified +/// @param mask Input Mask as a 2D vector. Used only if prev argument specified +/// @param lower Input Lower limit on the values in histogram (default numeric_limit::min() on axes) +/// @param upper Input Upper limit on the values in histogram (default numeric_limit::max() on axes) +/// @param setlimits Input Use specified limits if this is true (default is false) +/// \brief Overload version of fill_histogram +/// +/// Takes a third argument to determine whether to clear container before filling. +/// For eg, when there is a need to accumulate the histograms do +/// \code +/// fill_histogram(view(img), hist, true); +/// \endcode +/// +template +void fill_histogram( + SrcView const& srcview, + histogram& hist, + std::size_t bin_width = 1, + bool accumulate = false, + bool sparsefill = true, + bool applymask = false, + std::vector> mask = {}, + typename histogram::key_type lower = + detail::tuple_limit::key_type>::min(), + typename histogram::key_type upper = + detail::tuple_limit::key_type>::max(), + bool setlimits = false) +{ + if (!accumulate) + hist.clear(); + + detail::filler::dimension()> f; + if (!sparsefill) + f(hist, lower, upper, bin_width); + + hist.template fill(srcview, bin_width, applymask, mask, lower, upper, setlimits); +} + +/// +/// \fn void cumulative_histogram(Container&) +/// \ingroup Histogram Algorithms +/// \tparam Container Input histogram container +/// \brief Optionally overload this function with any external histogram class +/// +/// Cumulative histogram is calculated over any arbitrary dimensional +/// histogram. The only tradeoff could be the runtime complexity which in +/// the worst case would be max( #pixel_values , #bins ) * #dimensions. +/// For single dimensional histograms the complexity has been brought down to +/// #bins * log( #bins ) by sorting the keys and then calculating the cumulative version. +/// +template +Container cumulative_histogram(Container const&); + +template +histogram cumulative_histogram(histogram const& hist) +{ + using check_list = boost::mp11::mp_list...>; + static_assert( + boost::mp11::mp_all_of::value, + "Cumulative histogram not possible of this type"); + + using histogram_t = histogram; + using pair_t = std::pair; + using value_t = typename histogram_t::value_type; + + histogram_t cumulative_hist; + std::size_t const dims = histogram_t::dimension(); + if (dims == 1) + { + std::vector sorted_keys(hist.size()); + std::size_t counter = 0; + std::for_each(hist.begin(), hist.end(), [&](value_t const& v) { + sorted_keys[counter++] = std::make_pair(v.first, v.second); + }); + std::sort(sorted_keys.begin(), sorted_keys.end()); + auto cumulative_counter = static_cast(0); + for (std::size_t i = 0; i < sorted_keys.size(); ++i) + { + cumulative_counter += sorted_keys[i].second; + cumulative_hist[(sorted_keys[i].first)] = cumulative_counter; + } + } + else + { + std::for_each(hist.begin(), hist.end(), [&](value_t const& v1) { + auto cumulative_counter = static_cast(0); + std::for_each(hist.begin(), hist.end(), [&](value_t const& v2) { + bool comp = detail::tuple_compare( + v2.first, v1.first, + boost::mp11::make_index_sequence{}); + if (comp) + cumulative_counter += hist.at(v2.first); + }); + cumulative_hist[v1.first] = cumulative_counter; + }); + } + return cumulative_hist; +} + +}} //namespace boost::gil + +#endif diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index ed3b36f497..6abbfdee88 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -39,3 +39,4 @@ add_subdirectory(image) add_subdirectory(image_view) add_subdirectory(algorithm) add_subdirectory(image_processing) +add_subdirectory(histogram) diff --git a/test/core/Jamfile b/test/core/Jamfile index 7ced838338..45992e30ee 100644 --- a/test/core/Jamfile +++ b/test/core/Jamfile @@ -32,3 +32,4 @@ build-project image ; build-project image_view ; build-project algorithm ; build-project image_processing ; +build-project histogram ; diff --git a/test/core/histogram/CMakeLists.txt b/test/core/histogram/CMakeLists.txt new file mode 100644 index 0000000000..42a4931cfd --- /dev/null +++ b/test/core/histogram/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# Copyright 2020 Debabrata Mandal +# +# Distributed under 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 +# + +foreach(_name + access + constructor + cumulative + dimension + fill + hash_tuple + helpers + is_compatible + key + sub_histogram + utilities) + set(_test t_core_histogram_${_name}) + set(_target test_core_histogram_${_name}) + + add_executable(${_target} "") + target_sources(${_target} PRIVATE ${_name}.cpp) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK) + add_test(NAME ${_test} COMMAND ${_target}) + + unset(_name) + unset(_target) + unset(_test) +endforeach() diff --git a/test/core/histogram/Jamfile b/test/core/histogram/Jamfile new file mode 100644 index 0000000000..883fba7096 --- /dev/null +++ b/test/core/histogram/Jamfile @@ -0,0 +1,21 @@ +# +# Copyright 2020 Debabrata Mandal +# +# Distributed under 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 +# + +import testing ; + +compile constructor.cpp ; +compile dimension.cpp ; +run access.cpp ; +run cumulative.cpp ; +run fill.cpp ; +run hash_tuple.cpp ; +run helpers.cpp ; +run is_compatible.cpp ; +run key.cpp ; +run sub_histogram.cpp ; +run utilities.cpp ; diff --git a/test/core/histogram/access.cpp b/test/core/histogram/access.cpp new file mode 100644 index 0000000000..0c1847e042 --- /dev/null +++ b/test/core/histogram/access.cpp @@ -0,0 +1,35 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 + +namespace gil = boost::gil; + +void check_indexing_operator() +{ + gil::histogram h1; + h1(1) = 3; + BOOST_TEST(h1(1) == 3); + BOOST_TEST(h1(3) == 0); + + gil::histogram h2; + h2(1, 'a', "A") = 4; + BOOST_TEST(h2(1, 'a', "A") == 4); + BOOST_TEST(h2(1, 'a', "B") == 0); +} + +int main() { + + check_indexing_operator(); + + return boost::report_errors(); +} diff --git a/test/core/histogram/constructor.cpp b/test/core/histogram/constructor.cpp new file mode 100644 index 0000000000..92817b8f9a --- /dev/null +++ b/test/core/histogram/constructor.cpp @@ -0,0 +1,31 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 + +namespace gil = boost::gil; +namespace mp11 = boost::mp11; + +void check_histogram_constructors() +{ + gil::histogram h1; + gil::histogram h2 = h1; + gil::histogram h3; + gil::histogram h4(h3); + + gil::histogram d1, d2 = d1, d3(d2); +} + +int main() { + + check_histogram_constructors(); + return 0; + +} diff --git a/test/core/histogram/cumulative.cpp b/test/core/histogram/cumulative.cpp new file mode 100644 index 0000000000..c06f710890 --- /dev/null +++ b/test/core/histogram/cumulative.cpp @@ -0,0 +1,54 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 + +namespace gil = boost::gil; + +void check_cumulative() +{ + gil::histogram h1; + for (int i = 0; i < 8; i++) + { + h1(i) = 1; + } + auto h2 = cumulative_histogram(h1); + bool check1 = true; + for (int i = 0; i < 8; i++) + { + if(h2(i) != i+1) + check1 = false; + } + BOOST_TEST(check1); + + gil::histogram h3; + h3(1, 3) = 1; + h3(1, 4) = 2; + h3(2, 1) = 3; + h3(2, 2) = 1; + h3(2, 5) = 2; + h3(3, 2) = 3; + h3(3, 9) = 1; + auto h4 = cumulative_histogram(h3); + BOOST_TEST(h4(1, 3) == 1); + BOOST_TEST(h4(1, 4) == 3); + BOOST_TEST(h4(2, 1) == 3); + BOOST_TEST(h4(2, 2) == 4); + BOOST_TEST(h4(2, 5) == 9); + BOOST_TEST(h4(3, 2) == 7); + BOOST_TEST(h4(3, 9) == 13); +} + +int main() { + + check_cumulative(); + + return boost::report_errors(); +} diff --git a/test/core/histogram/dimension.cpp b/test/core/histogram/dimension.cpp new file mode 100644 index 0000000000..912f15d27f --- /dev/null +++ b/test/core/histogram/dimension.cpp @@ -0,0 +1,31 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 + +namespace gil = boost::gil; +namespace mp11 = boost::mp11; + +void check_histogram_constructors() +{ + gil::histogram h1; + gil::histogram h2 = h1; + gil::histogram h3; + gil::histogram h4(h3); + + static_assert(h1.dimension() == h2.dimension(),"Dimension mismatch"); + static_assert(h1.dimension() != h3.dimension(),"Dimension mismatch"); + static_assert(h3.dimension() == h4.dimension(),"Dimension mismatch"); +} + +int main() { + + check_histogram_constructors(); + return 0; + +} diff --git a/test/core/histogram/fill.cpp b/test/core/histogram/fill.cpp new file mode 100644 index 0000000000..ac43dc1f5a --- /dev/null +++ b/test/core/histogram/fill.cpp @@ -0,0 +1,282 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 +#include + +namespace gil = boost::gil; +namespace mp11 = boost::mp11; + +gil::gray8_image_t img1(4, 4, gil::gray8_pixel_t(1)); +gil::gray8_view_t v1 = view(img1); + +gil::rgb8_image_t img2(4, 4, gil::rgb8_pixel_t(1)); +gil::rgb8_view_t v2 = view(img2); + +std::uint8_t sparse_matrix[] = +{ + 1, 1, 1, 1, + 3, 3, 3, 3, + 5, 5, 5, 5, + 7, 7, 7, 7 +}; + +std::uint8_t big_matrix[] = +{ + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 1, 2, 1, 2, 1, 2, + 1, 2, 3, 4, 5, 6, 7, 8, + 3, 4, 3, 4, 3, 4, 3, 4, + 1, 2, 3, 4, 5, 6, 7, 8, + 5, 6, 5, 6, 5, 6, 5, 6, + 1, 2, 3, 4, 5, 6, 7, 8, + 7, 8, 7, 8, 7, 8, 7, 8 +}; + +std::uint8_t big_rgb_matrix[] = +{ + 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, 7, 8, 9, 8, 9, 10, + 1, 2, 3, 2, 3, 4, 1, 2, 3, 2, 3, 4, 1, 2, 3, 2, 3, 4, 1, 2, 3, 2, 3, 4, + 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, 7, 8, 9, 8, 9, 10, + 3, 4, 5, 4, 5, 6, 3, 4, 5, 4, 5, 6, 3, 4, 5, 4, 5, 6, 3, 4, 5, 4, 5, 6, + 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, 7, 8, 9, 8, 9, 10, + 5, 6, 7, 6, 7, 8, 5, 6, 7, 6, 7, 8, 5, 6, 7, 6, 7, 8, 5, 6, 7, 6, 7, 8, + 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, 7, 8, 9, 8, 9, 10, + 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, +}; + +std::vector> mask = +{ + {1, 0, 0, 1}, + {0, 0, 1, 1}, + {0, 1, 0, 1}, + {1, 1, 0, 0}, +}; + +gil::gray8c_view_t sparse_gray_view = gil::interleaved_view(4, 4, reinterpret_cast(sparse_matrix), 4); + +gil::gray8c_view_t big_gray_view = gil::interleaved_view(8, 8, reinterpret_cast(big_matrix), 8); + +gil::rgb8c_view_t big_rgb_view = gil::interleaved_view(8, 8, reinterpret_cast(big_rgb_matrix), 24); + +void check_histogram_fill_test1() +{ + gil::histogram h1; + + h1.fill(big_gray_view); + + bool check_gray_fill = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(h1(i) != 8) + { + check_gray_fill = false; + } + } + BOOST_TEST(check_gray_fill); +} + +void check_histogram_fill_test2() +{ + gil::histogram h3; + h3.fill(big_rgb_view); + + bool check_rgb_fill = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(h3(i, i+1, i+2) != 8) + { + check_rgb_fill = false; + } + } + BOOST_TEST(check_rgb_fill); +} + +void check_histogram_fill_test3() +{ + gil::histogram h2; + h2.fill<1>(big_rgb_view); + bool check_gray_fill2 = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(h2(i+1) != 8) + { + check_gray_fill2 = false; + } + } + BOOST_TEST(check_gray_fill2); +} + +void check_histogram_fill_test4() +{ + gil::histogram h1; + // Check with limits + std::tuple lower{2}, higher{6}; + h1.clear(); + h1.fill(big_gray_view, 1, false, {{}}, lower, higher, true); + bool check_gray_fill = true; + check_gray_fill = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(i<2 || i>6) + { + check_gray_fill = check_gray_fill & (h1(i)==0);continue; + } + if(h1(i) != 8) + { + check_gray_fill = false; + } + } + BOOST_TEST(check_gray_fill); +} + +void check_histogram_fill_test5() +{ + gil::histogram h3; + std::tuple lower1{2,2,2}, higher1{6,6,6}; + h3.clear(); + h3.fill(big_rgb_view, 1, false, {{}}, lower1, higher1, true); + + bool check_rgb_fill = true; + check_rgb_fill = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(!(i >= 2 && (i+2) <= 6)) + { + check_rgb_fill = check_rgb_fill & (h3(i, i+1, i+2)==0);continue; + } + if(h3(i, i+1, i+2) != 8) + { + check_rgb_fill = false; + } + } + BOOST_TEST(check_rgb_fill); +} + +void check_histogram_fill_test6() +{ + gil::histogram h2; + h2.clear(); + std::tuple lower{2}, higher{6}; + h2.fill<1>(big_rgb_view, 1, false, {{}}, lower, higher, true); + bool check_gray_fill2 = true; + check_gray_fill2 = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(i+1 < 2 || i+1 > 6) + { + check_gray_fill2 = check_gray_fill2 & (h2(i+1)==0);continue; + } + if(h2(i+1) != 8) + { + check_gray_fill2 = false; + } + } + BOOST_TEST(check_gray_fill2); +} + +void check_histogram_fill_test7() +{ + //Check masking + gil::histogram h4; + std::tuple low{1}, high{8}; + gil::fill_histogram(sparse_gray_view, h4, 1, false, false, true, mask, low, high, true); + + bool check_1d = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(i%2==1) + { + check_1d = check_1d & (h4(i)==2); + } + } + BOOST_TEST(check_1d); +} + +void check_histogram_fill_algorithm() +{ + gil::histogram h1; + + gil::fill_histogram<1>(big_rgb_view, h1); + + bool check_1d = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(h1(i+1) != 8) + { + check_1d = false; + } + } + BOOST_TEST(check_1d); + + gil::histogram h2; + + gil::fill_histogram<2, 1>(big_rgb_view, h2); + + bool check_2d = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(h2(i+2, i+1) != 8) + { + check_2d = false; + } + } + BOOST_TEST(check_2d); + + gil::histogram h3; + + std::tuple low(1), high(8); + gil::fill_histogram(sparse_gray_view, h3, 1, false, false, false, {{}}, low, high, true); + + check_1d = true; + for (std::size_t i = 1; i <= 8; ++i) + { + if(h3.find(std::tuple(i)) == h3.end()) + { + check_1d = false; + } + else + { + check_1d = check_1d & (i % 2 == 1 ? (h3(i) == 4) : (h3(i) == 0)); + } + } + BOOST_TEST(check_1d); +} + +void check_fill_bin_width() +{ + gil::histogram h1; + gil::fill_histogram(big_gray_view, h1, 2); + bool check1 = true; + for(std::size_t i = 1; i <= 3; ++i) + { + check1 = check1 & (h1(i) == 16); + } + check1 = check1 & (h1(0) == 8) & (h1(4) == 8); + BOOST_TEST(check1); +} + +int main() { + + check_histogram_fill_test1(); + check_histogram_fill_test2(); + check_histogram_fill_test3(); + check_histogram_fill_test4(); + check_histogram_fill_test5(); + check_histogram_fill_test6(); + check_histogram_fill_test7(); + check_histogram_fill_algorithm(); + check_fill_bin_width(); + + return boost::report_errors(); +} \ No newline at end of file diff --git a/test/core/histogram/hash_tuple.cpp b/test/core/histogram/hash_tuple.cpp new file mode 100644 index 0000000000..69d47d16a2 --- /dev/null +++ b/test/core/histogram/hash_tuple.cpp @@ -0,0 +1,35 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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; +namespace mp11 = boost::mp11; + +void check_detail_hash_tuple () +{ + std::tuple t(1, 1); + std::size_t seed1 = 0; + boost::hash_combine(seed1, std::get<0>(t)); + boost::hash_combine(seed1, std::get<1>(t)); + + gil::detail::hash_tuple g; + std::size_t seed2 = g(t); + BOOST_TEST(seed1 == seed2); +} + +int main() +{ + check_detail_hash_tuple(); + return boost::report_errors(); +} diff --git a/test/core/histogram/helpers.cpp b/test/core/histogram/helpers.cpp new file mode 100644 index 0000000000..38fafd9f2a --- /dev/null +++ b/test/core/histogram/helpers.cpp @@ -0,0 +1,113 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 + +#include + +namespace gil = boost::gil; +namespace mp11 = boost::mp11; + +void check_helper_fn_pixel_to_tuple() +{ + gil::gray8_pixel_t g1(2); + auto g2 = gil::detail::pixel_to_tuple(g1, mp11::make_index_sequence<1>{}); + + bool const same_gray_type = std::is_same, decltype(g2)>::value; + BOOST_TEST(same_gray_type); + BOOST_TEST(g1[0] == std::get<0>(g2)); + + gil::rgb8_pixel_t r1(1,2,3); + auto r2 = gil::detail::pixel_to_tuple(r1, mp11::index_sequence<0, 1, 2>{}); + + bool const same_rgb_type = std::is_same, + decltype(r2)>::value; + BOOST_TEST(same_rgb_type); + BOOST_TEST(r1[0] == std::get<0>(r2) && r1[1] == std::get<1>(r2) && r1[2] == std::get<2>(r2)); + + auto r3 = gil::detail::pixel_to_tuple(r1, mp11::index_sequence<1, 2, 0>{}); + BOOST_TEST(r1[0] == std::get<2>(r3) && r1[1] == std::get<0>(r3) && r1[2] == std::get<1>(r3)); +} + +void check_helper_fn_tuple_to_tuple() +{ + std::tuple t1(1); + auto t2 = gil::detail::tuple_to_tuple(t1, mp11::make_index_sequence<1>{}); + + bool const same_gray_type = std::is_same, decltype(t2)>::value; + BOOST_TEST(same_gray_type); + BOOST_TEST(std::get<0>(t1) == std::get<0>(t2)); + + std::tuple r1(1, 2, "A"); + auto r2 = gil::detail::tuple_to_tuple(r1, mp11::index_sequence<0, 1, 2>{}); + + bool const same_rgb_type = std::is_same, + decltype(r2)>::value; + BOOST_TEST(same_rgb_type); + BOOST_TEST( std::get<0>(r1) == std::get<0>(r2) && + std::get<1>(r1) == std::get<1>(r2) && + std::get<2>(r1) == std::get<2>(r2)); + + auto r3 = gil::detail::tuple_to_tuple(r1, mp11::index_sequence<1, 2, 0>{}); + BOOST_TEST( std::get<0>(r1) == std::get<2>(r3) && + std::get<1>(r1) == std::get<0>(r3) && + std::get<2>(r1) == std::get<1>(r3)); +} + +void check_helper_tuple_limit() +{ + using type1 = std::tuple; + using type2 = std::tuple; + type1 t1_min(std::numeric_limits::min(), std::numeric_limits::min()); + type1 t1_max(std::numeric_limits::max(), std::numeric_limits::max()); + type2 t2_min(std::numeric_limits::min(), std::numeric_limits::min()); + type2 t2_max(std::numeric_limits::max(), std::numeric_limits::max()); + + BOOST_TEST(t1_min == gil::detail::tuple_limit::min()); + BOOST_TEST(t1_max == gil::detail::tuple_limit::max()); + BOOST_TEST(t2_min == gil::detail::tuple_limit::min()); + BOOST_TEST(t2_max == gil::detail::tuple_limit::max()); + +} + +void check_filler() +{ + boost::gil::histogram h; + boost::gil::detail::filler<1> f; + std::tuple l1{4}, h1{13}; + f(h, l1, h1); + + std::tuple l2{20}, h2{33}; + f(h, l2, h2); + + bool check = true; + for(int i = 0; i < 100; i++) + { + if((i >= 4 && i <= 13) || (i >= 20 && i <= 33)) + { + if(h.find(std::tuple(i))==h.end()) + check = false; + } + } + BOOST_TEST(check); +} + +int main() { + + check_helper_fn_pixel_to_tuple(); + check_helper_fn_tuple_to_tuple(); + check_helper_tuple_limit(); + check_filler(); + + return boost::report_errors(); +} diff --git a/test/core/histogram/is_compatible.cpp b/test/core/histogram/is_compatible.cpp new file mode 100644 index 0000000000..bb150943a9 --- /dev/null +++ b/test/core/histogram/is_compatible.cpp @@ -0,0 +1,72 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 + +namespace gil = boost::gil; +namespace mp11 = boost::mp11; + + +void check_is_pixel_compatible() +{ + gil::histogram h1; + gil::histogram h2; + gil::histogram h3; + gil::histogram h4; + + BOOST_TEST(h1.is_pixel_compatible()); + BOOST_TEST(h2.is_pixel_compatible()); + BOOST_TEST(h3.is_pixel_compatible()); + BOOST_TEST(!h4.is_pixel_compatible()); +} + +void check_is_tuple_compatible() +{ + gil::histogram h1; + gil::histogram h2; + gil::histogram h3; + gil::histogram h4; + gil::histogram h5; + + std::tuple t1; + std::tuple t2; + std::tuple t3; + std::tuple t4; + std::tuple t5; + + BOOST_TEST(h1.is_tuple_compatible(t1)); + BOOST_TEST(h1.is_tuple_compatible(t2)); + BOOST_TEST(!h1.is_tuple_compatible(t3)); + BOOST_TEST(!h1.is_tuple_compatible(t5)); + + BOOST_TEST(h2.is_tuple_compatible(t1)); + BOOST_TEST(h2.is_tuple_compatible(t2)); + BOOST_TEST(!h2.is_tuple_compatible(t3)); + + BOOST_TEST(!h3.is_tuple_compatible(t1)); + BOOST_TEST(h3.is_tuple_compatible(t3)); + BOOST_TEST(h3.is_tuple_compatible(t4)); + BOOST_TEST(!h3.is_tuple_compatible(t5)); + + BOOST_TEST(!h4.is_tuple_compatible(t1)); + BOOST_TEST(h4.is_tuple_compatible(t3)); + BOOST_TEST(h4.is_tuple_compatible(t4)); + BOOST_TEST(!h4.is_tuple_compatible(t5)); +} + +int main() { + + check_is_pixel_compatible(); + check_is_tuple_compatible(); + + return boost::report_errors(); +} diff --git a/test/core/histogram/key.cpp b/test/core/histogram/key.cpp new file mode 100644 index 0000000000..e4e2418aef --- /dev/null +++ b/test/core/histogram/key.cpp @@ -0,0 +1,63 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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; + +void check_histogram_key_from_tuple() +{ + gil::histogram h1; + std::tuple t1(1, 2); + auto t2 = h1.key_from_tuple(t1); + const bool same_type = std::is_same, decltype(t2)>::value; + + BOOST_TEST(same_type); + BOOST_TEST(std::get<0>(t2) == 1 && std::get<1>(t2) == 2); + + std::tuple t3(1, 2, 4, 2); + auto t4 = h1.key_from_tuple<0, 2>(t3); + const bool same_type1 = std::is_same, decltype(t4)>::value; + + BOOST_TEST(same_type1); + BOOST_TEST(std::get<0>(t4) == 1 && std::get<1>(t4) == 4); +} + +void check_histogram_key_from_pixel() +{ + gil::histogram h1; + gil::gray8_pixel_t g1(1); + auto t1 = h1.key_from_pixel(g1); + const bool same_type = std::is_same, decltype(t1)>::value; + + BOOST_TEST(same_type); + BOOST_TEST(std::get<0>(t1) == 1); + + gil::histogram h2; + gil::rgb8_pixel_t r1(1, 0, 3); + auto t2 = h2.key_from_pixel<0, 2>(r1); + const bool same_type1 = std::is_same, decltype(t2)>::value; + + BOOST_TEST(same_type1); + BOOST_TEST(std::get<0>(t2) == 1 && std::get<1>(t2) == 3); +} + +int main() { + + check_histogram_key_from_tuple(); + check_histogram_key_from_pixel(); + + return boost::report_errors(); +} + \ No newline at end of file diff --git a/test/core/histogram/sub_histogram.cpp b/test/core/histogram/sub_histogram.cpp new file mode 100644 index 0000000000..af618b2334 --- /dev/null +++ b/test/core/histogram/sub_histogram.cpp @@ -0,0 +1,58 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 + +namespace gil = boost::gil; + +void check_sub_histogram_without_tuple() +{ + gil::histogram h; + h(1, 1, "A", 1) = 1; + h(1, 2, "B", 1) = 1; + h(2, 1, "C", 1) = 1; + h(2, 1, "D", 1) = 1; + h(2, 3, "E", 4) = 1; + auto h1 = h.sub_histogram<0,3>(); + BOOST_TEST(h1(1, 1) == 2); + BOOST_TEST(h1(2, 1) == 2); + BOOST_TEST(h1(2, 4) == 1); + BOOST_TEST(h1(5, 5) == 0); +} + +void check_sub_histogram_with_tuple() +{ + gil::histogram h; + h(1, 1, "A", 1) = 3; + h(1, 2, "C", 1) = 1; + h(2, 1, "C", 1) = 1; + h(2, 1, "A", 1) = 1; + h(2, 3, "E", 4) = 1; + h(1, 3, "A", 1) = 2; + std::tuple t(1.0, 1000, "A", 1); + // This means 1st dimension is useless for matching. + auto h1 = h.sub_histogram<0,2,3>(t, t); + BOOST_TEST(h1(1, 1, "A", 1) == 3); + BOOST_TEST(h1(1, 2, "C", 1) == 0); + BOOST_TEST(h1(2, 1, "C", 1) == 0); + BOOST_TEST(h1(2, 1, "A", 1) == 0); + BOOST_TEST(h1(2, 1, "A", 1) == 0); + BOOST_TEST(h1(1, 3, "A", 1) == 2); +} + +int main() { + + check_sub_histogram_without_tuple(); + check_sub_histogram_with_tuple(); + + return boost::report_errors(); +} diff --git a/test/core/histogram/utilities.cpp b/test/core/histogram/utilities.cpp new file mode 100644 index 0000000000..4f26a05b41 --- /dev/null +++ b/test/core/histogram/utilities.cpp @@ -0,0 +1,214 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 +#include +#include + +namespace gil = boost::gil; +namespace mp11 = boost::mp11; + +std::uint8_t big_matrix[] = +{ + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 1, 2, 1, 2, 1, 2, + 1, 2, 3, 4, 5, 6, 7, 8, + 3, 4, 3, 4, 3, 4, 3, 4, + 1, 2, 3, 4, 5, 6, 7, 8, + 5, 6, 5, 6, 5, 6, 5, 6, + 1, 2, 3, 4, 5, 6, 7, 8, + 7, 8, 7, 8, 7, 8, 7, 8 +}; + +void check_normalize() +{ + auto epsilon = 1e-6; + // 1D histogram + double expected[64]; + gil::histogram h1; + int sum = 0; + for (std::size_t i = 0; i < 64; i++) + { + h1(i) = big_matrix[i]; + sum += big_matrix[i]; + } + for (std::size_t i = 0; i < 64; i++) + { + expected[i] = double(big_matrix[i]) / sum; + } + h1.normalize(); + + bool check = true; + for (std::size_t i = 0; i < 64; i++) + { + check = check & (abs(expected[i] - h1(i)) < epsilon); + } + BOOST_TEST(check); + + // 2D histogram + double expected2[8][8]; + gil::histogram h2; + int sum2 = 0; + for (std::size_t i = 0; i < 64; i++) + { + h2(i/8, i%8) = big_matrix[i]; + sum2 += big_matrix[i]; + } + for (std::size_t i = 0; i < 64; i++) + { + expected2[i/8][i%8] = double(big_matrix[i]) / sum2; + } + h2.normalize(); + + bool check2 = true; + for (std::size_t i = 0; i < 64; i++) + { + check2 = check2 & (abs(expected2[i/8][i%8] - h2(i/8,i%8)) < epsilon); + } + BOOST_TEST(check2); +} + +void check_nearest_key() +{ + { + gil::histogram h1; + h1(1) = 1; + h1(3) = 4; + h1(4) = 4; + h1(6) = 1; + std::tuple k1{2}, k2{3}, k3{5}; + std::tuple k1_expected{1}, k2_expected{3}, k3_expected{4}; + BOOST_TEST(k1_expected == h1.nearest_key(k1)); + BOOST_TEST(k2_expected == h1.nearest_key(k2)); + BOOST_TEST(k3_expected == h1.nearest_key(k3)); + } + + { + gil::histogram h2; + h2(1, 1) = 1; + h2(1, 4) = 1; + h2(2, 4) = 1; + h2(4, 4) = 1; + std::tuple k1(1, 1), k2(1, 3), k3(2, 1), k4(2, 7), k5(4, 4); + std::tuple k1_exp(1, 1), k2_exp(1, 1), k3_exp(1, 4), k4_exp(2, 4), k5_exp(4, 4); + BOOST_TEST(k1_exp == h2.nearest_key(k1)); + BOOST_TEST(k2_exp == h2.nearest_key(k2)); + BOOST_TEST(k3_exp == h2.nearest_key(k3)); + BOOST_TEST(k4_exp == h2.nearest_key(k4)); + BOOST_TEST(k5_exp == h2.nearest_key(k5)); + } + +} + +void check_equals() +{ + gil::histogram h, h2; + h(1) = 3; + h(4) = 1; + h(2) = 6; + h(7) = 3; + h(9) = 7; + h2 = h; + BOOST_TEST(h2.equals(h)); + + gil::histogram h3; + h3(1) = 3; + h3(4) = 1; + h3(2) = 6; + h3(7) = 3; + h3(9) = 7; + BOOST_TEST(h3.equals(h)); +} + +void check_sum() +{ + gil::histogram h; + h(1) = 3; + h(4) = 1; + h(2) = 6; + h(7) = 3; + h(9) = 7; + auto sm = h.sum(); + BOOST_TEST(sm == 20); +} + +void check_max_key() +{ + gil::histogram h; + h(1) = 3; + h(4) = 1; + h(2) = 6; + h(7) = 3; + h(9) = 7; + BOOST_TEST(std::get<0>(h.max_key()) == 9); + + gil::histogram h2; + h2(1, 4) = 3; + h2(4, 2) = 1; + h2(2, 5) = 6; + h2(7, 4) = 3; + h2(9, 1) = 7; + h2(9, 3) = 7; + BOOST_TEST(std::get<0>(h2.max_key()) == 9 && std::get<1>(h2.max_key()) == 3); +} + +void check_min_key() +{ + gil::histogram h; + h(1) = 3; + h(4) = 1; + h(2) = 6; + h(7) = 3; + h(9) = 7; + BOOST_TEST(std::get<0>(h.min_key()) == 1); + + gil::histogram h2; + h2(1, 4) = 3; + h2(4, 2) = 1; + h2(2, 5) = 6; + h2(7, 4) = 3; + h2(9, 1) = 7; + h2(9, 3) = 7; + BOOST_TEST(std::get<0>(h2.min_key()) == 1 && std::get<1>(h2.min_key()) == 4); +} + +void check_sorted_keys() +{ + gil::histogram h; + h(1) = 3; + h(4) = 1; + h(2) = 6; + h(7) = 3; + h(9) = 7; + + std::vector> v; + v.push_back(std::tuple(1)); + v.push_back(std::tuple(2)); + v.push_back(std::tuple(4)); + v.push_back(std::tuple(7)); + v.push_back(std::tuple(9)); + BOOST_TEST(v == h.sorted_keys()); +} + +int main() { + + check_normalize(); + check_nearest_key(); + check_equals(); + check_max_key(); + check_min_key(); + check_sum(); + check_sorted_keys(); + + return boost::report_errors(); +} diff --git a/test/extension/CMakeLists.txt b/test/extension/CMakeLists.txt index b642d2b296..9f7883066f 100644 --- a/test/extension/CMakeLists.txt +++ b/test/extension/CMakeLists.txt @@ -9,6 +9,10 @@ if(BOOST_GIL_ENABLE_EXT_DYNAMIC_IMAGE) add_subdirectory(dynamic_image) endif() +if(BOOST_GIL_ENABLE_EXT_HISTOGRAM) + add_subdirectory(histogram) +endif() + if(BOOST_GIL_ENABLE_EXT_NUMERIC) add_subdirectory(numeric) endif() diff --git a/test/extension/Jamfile b/test/extension/Jamfile index cf9ef40896..5754513279 100644 --- a/test/extension/Jamfile +++ b/test/extension/Jamfile @@ -7,6 +7,7 @@ # copy at http://www.boost.org/LICENSE_1_0.txt) build-project dynamic_image ; +build-project histogram ; build-project numeric ; build-project toolbox ; build-project io ; diff --git a/test/extension/histogram/CMakeLists.txt b/test/extension/histogram/CMakeLists.txt new file mode 100644 index 0000000000..562d4d916c --- /dev/null +++ b/test/extension/histogram/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright 2020 Debabrata Mandal +# +# Distributed under 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 +# + +foreach(_name + histogram) + set(_test t_core_histogram_${_name}) + set(_target test_core_histogram_${_name}) + + add_executable(${_target} "") + target_sources(${_target} PRIVATE ${_name}.cpp) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK) + add_test(NAME ${_test} COMMAND ${_target}) + + unset(_name) + unset(_target) + unset(_test) +endforeach() diff --git a/test/extension/histogram/Jamfile b/test/extension/histogram/Jamfile new file mode 100644 index 0000000000..d7c7edf238 --- /dev/null +++ b/test/extension/histogram/Jamfile @@ -0,0 +1,11 @@ +# +# Copyright 2020 Debabrata Mandal +# +# Distributed under 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 +# + +import testing ; + +run histogram.cpp ; diff --git a/test/extension/histogram/histogram.cpp b/test/extension/histogram/histogram.cpp new file mode 100644 index 0000000000..6b4b23951e --- /dev/null +++ b/test/extension/histogram/histogram.cpp @@ -0,0 +1,176 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 + +// Supported Container types for histogram +#include +#include +#include +#include + +// Basic tests to make sure compatible container types produce +// expected output histogram. + +namespace gil = boost::gil; + +gil::gray8_image_t img1(4, 4, gil::gray8_pixel_t(1)); +gil::gray8_view_t v1 = view(img1); + +gil::rgb8_image_t img2(4, 4, gil::rgb8_pixel_t(1)); +gil::rgb8_view_t v2 = view(img2); + +gil::gray16_image_t img3(4, 4, gil::gray16_pixel_t(1)); +gil::gray16_view_t v3 = view(img3); + +template +bool check_equal(T &cont1, T &cont2, std::size_t size) +{ + bool ch = true; + for(std::size_t i = 0; i < size; ++i) + { + ch = ch & (cont1[i] == cont2[i]); + } + return ch; +} + +template +bool check_equal(T &cont1, T &cont2) +{ + bool ch = true; + for(auto &it : cont1) + { + ch = ch & (cont1[it.first] == cont2[it.first]); + } + return ch; +} + +void check_fill_histogram_vector() +{ + std::vector c1, c1_expected(256,0); + c1_expected[1] = 16; + gil::fill_histogram(v1, c1); + BOOST_TEST(check_equal(c1, c1_expected, c1_expected.size())); + + c1_expected[1] = 32; + gil::fill_histogram(v1, c1, true); + BOOST_TEST(check_equal(c1, c1_expected, c1_expected.size())); + + std::vector c2, c2_expected; + gil::fill_histogram(v2, c2); + gil::fill_histogram(gil::color_converted_view(v2), c2_expected); + BOOST_TEST(check_equal(c2, c2_expected, c2_expected.size())); +} + + +void check_fill_histogram_array() +{ + std::array c1{0}, c1_expected{0}; + c1_expected[1] = 16; + gil::fill_histogram(v1, c1); + BOOST_TEST(check_equal(c1, c1_expected, c1_expected.size())); + + c1_expected[1] = 32; + gil::fill_histogram(v1, c1, true); + BOOST_TEST(check_equal(c1, c1_expected, c1_expected.size())); + + std::array c2{0}, c2_expected{0}; + gil::fill_histogram(v1, c2); + gil::fill_histogram(gil::color_converted_view(v2), c2_expected); + BOOST_TEST(check_equal(c2, c2_expected, c2_expected.size())); + + // Check binning + std::array c3{0}, c3_expected{0}; + c3_expected[0] = 16; + gil::fill_histogram(v3, c3); + BOOST_TEST(check_equal(c3, c3_expected, c3_expected.size())); +} + +void check_fill_histogram_map() +{ + std::map c1, c1_expected; + c1_expected[1] = 16; + gil::fill_histogram(v1, c1); + BOOST_TEST(check_equal(c1, c1_expected)); + + c1_expected[1] = 32; + gil::fill_histogram(v1, c1, true); + BOOST_TEST(check_equal(c1, c1_expected)); + + std::map c2, c2_expected; + gil::fill_histogram(v2, c2); + gil::fill_histogram(gil::color_converted_view(v2), c2_expected); + BOOST_TEST(check_equal(c2, c2_expected)); +} + +void check_cumulative_histogram_vector() +{ + std::vector v(8); + for (std::size_t i = 0; i < v.size(); i++) + { + v[i] = 1; + } + auto v1 = gil::cumulative_histogram(v); + bool check = true; + for (std::size_t i = 0; i < v.size(); i++) + { + if(v1[i] != int(i) + 1) + check = false; + } + BOOST_TEST(check); +} + +void check_cumulative_histogram_array() +{ + std::array arr; + for (std::size_t i = 0; i < arr.size(); i++) + { + arr[i] = 1; + } + auto arr1 = gil::cumulative_histogram(arr); + bool check = true; + for (std::size_t i = 0; i < arr.size(); i++) + { + if(arr1[i] != int(i) + 1) + check = false; + } + BOOST_TEST(check); +} + +void check_cumulative_histogram_map() +{ + std::map mp; + for (std::size_t i = 0; i < 8; i++) + { + mp[i] = 1; + } + auto mp1 = gil::cumulative_histogram(mp); + bool check = true; + for (std::size_t i = 0; i < mp.size(); i++) + { + if(mp1[i] != int(i) + 1) + check = false; + } + BOOST_TEST(check); +} + +int main() +{ + check_fill_histogram_vector(); + check_fill_histogram_array(); + check_fill_histogram_map(); + + check_cumulative_histogram_vector(); + check_cumulative_histogram_array(); + check_cumulative_histogram_map(); + + return boost::report_errors(); +} From fb7512c29f5c3ac3f8b44ce5925757855242d489 Mon Sep 17 00:00:00 2001 From: Debabrata Mandal <32168969+codejaeger@users.noreply.github.com> Date: Mon, 25 Jan 2021 02:02:39 +0530 Subject: [PATCH 016/193] Add histogram equalization feature (#514) Co-authored-by: codejaeger --- .../contrast_enhancement/barbara.jpg | Bin 0 -> 24897 bytes .../contrast_enhancement/church.jpg | Bin 0 -> 34771 bytes .../contrast_enhancement/he_chart.png | Bin 0 -> 14693 bytes .../histogram_equalization.rst | 100 +++++++ example/histogram_equalization.cpp | 20 ++ .../histogram_equalization.hpp | 150 ++++++++++ .../histogram_equalization.cpp | 280 ++++++++++++++++++ 7 files changed, 550 insertions(+) create mode 100644 doc/image_processing/contrast_enhancement/barbara.jpg create mode 100644 doc/image_processing/contrast_enhancement/church.jpg create mode 100644 doc/image_processing/contrast_enhancement/he_chart.png create mode 100644 doc/image_processing/contrast_enhancement/histogram_equalization.rst create mode 100644 example/histogram_equalization.cpp create mode 100644 include/boost/gil/image_processing/histogram_equalization.hpp create mode 100644 test/core/image_processing/histogram_equalization.cpp diff --git a/doc/image_processing/contrast_enhancement/barbara.jpg b/doc/image_processing/contrast_enhancement/barbara.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3dad19747e21279788ebcd70df7c3207482715eb GIT binary patch literal 24897 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 literal 0 HcmV?d00001 diff --git a/doc/image_processing/contrast_enhancement/church.jpg b/doc/image_processing/contrast_enhancement/church.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c0beb7c651a51c87fa91d265cf94b41bd2cb72fe GIT binary patch literal 34771 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%0Dn3>Q_=du7oL8mhQphw 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 LKPoBc7eD{m?VOo` literal 0 HcmV?d00001 diff --git a/doc/image_processing/contrast_enhancement/he_chart.png b/doc/image_processing/contrast_enhancement/he_chart.png new file mode 100644 index 0000000000000000000000000000000000000000..217c89ef1565ebdae0e03f2044aa931d954a35ed GIT binary patch literal 14693 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 literal 0 HcmV?d00001 diff --git a/doc/image_processing/contrast_enhancement/histogram_equalization.rst b/doc/image_processing/contrast_enhancement/histogram_equalization.rst new file mode 100644 index 0000000000..0874c1c4be --- /dev/null +++ b/doc/image_processing/contrast_enhancement/histogram_equalization.rst @@ -0,0 +1,100 @@ +.. _he: + +###################### +Histogram Equalization +###################### + +Description +----------- + +Histogram equalization also known as histogram flattening, is a non-linear image enhancement +algorithm that follows the idea that not only should an image cover the entire grayscale space +but also be uniformly distributed over that range. + +An ideal image would be the one having a flat histogram. + +Although care should be taken before applying a non-linear transformation on the image +histogram, there are good mathematical reasons why a flat histogram is the desired goal. + +A simple scenario would be an image with pixels concentrated in an interval, in which case +histogram equalization transforms pixels to achieve a flat histogram image. Thus enhancing +the image contrast. + +.. figure:: he_chart.png + :width: 200px + :align: center + :height: 100px + :alt: Could not load image. + :figclass: align-center + + Pixels concentrated in an interval spread out. + +Algorithm +--------- + +#. First calculate the histogram corresponding to input image. +#. If it is a multi channeled image (e.g. RGB), convert it to a independent color space + (like YCbCr, HSV etc.). +#. Then calculate the cumulative histogram over the input image. +#. Normalize the histogram to bring bin values between 0-1. For multi-channeled images + normalize each channel independently (by the number of pixels in image). +#. If the histogram of image is H(p\ :sub:`x`\) p\ :sub:`x`\ in [0, 255], then apply + the transformation p\ :sub:`x'`\ = H(p\ :sub:`x`\), p\ :sub:`x'`\ is pixel in output + image. + +**Explanation** + +Since we will be transforming the image to match a flat histogram, we match +the cumulative histogram of the image to the cumulative histogram of a flat histogram. + +Cumulative histogram of flat image is H(p\ :sub:`x'`\) = p\ :sub:`x'` . + +Hence, + + => H(p\ :sub:`x'`\) = H(p\ :sub:`x`\) + + => p\ :sub:`x'`\ = H(p\ :sub:`x`\) + +Results +------- +The algorithm is applied on a few standard images. One of the transformations in shown below: + +**Grayscale Image** + +.. figure:: barbara.jpg + :width: 512px + :align: center + :height: 256px + :alt: Could not load image. + :figclass: align-center + +**RGB** + +.. figure:: church.jpg + :width: 900px + :align: center + :height: 300px + :alt: Could not load image. + :figclass: align-center + + +Demo +---- + +Usage Syntax: + + .. code-block:: cpp + + gray8_image_t inp_img; + read_image("your_image.png", inp_img, png_tag{}); + gray8_image_t dst_img(inp_img.dimensions()); + histogram_equalization(view(inp_img), view(dst_img)); + + // To specify mask over input image + + vector> mask(inp_img.height(), vector(inp_img.width(), true)); + histogram_equalization(view(inp_img), view(dst_img), true, mask); + + .. tip:: Convert an RGB image to a channel independent color space + before trying the histogram equalization algorithm. + diff --git a/example/histogram_equalization.cpp b/example/histogram_equalization.cpp new file mode 100644 index 0000000000..e076f9f747 --- /dev/null +++ b/example/histogram_equalization.cpp @@ -0,0 +1,20 @@ +#include +#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()); + + // Consider changing image to independent color space, e.g. cmyk + boost::gil::histogram_equalization(view(img),view(img_out)); + + write_view("histogram_gray_equalized.png", view(img_out), png_tag{}); + + return 0; +} diff --git a/include/boost/gil/image_processing/histogram_equalization.hpp b/include/boost/gil/image_processing/histogram_equalization.hpp new file mode 100644 index 0000000000..d33c6f4663 --- /dev/null +++ b/include/boost/gil/image_processing/histogram_equalization.hpp @@ -0,0 +1,150 @@ +// +// Copyright 2020 Debabrata Mandal +// +// 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_HISTOGRAM_EQUALIZATION_HPP +#define BOOST_GIL_IMAGE_PROCESSING_HISTOGRAM_EQUALIZATION_HPP + +#include +#include + +#include +#include +#include + +namespace boost { namespace gil { + + +///////////////////////////////////////// +/// Histogram Equalization(HE) +///////////////////////////////////////// +/// \defgroup HE HE +/// \brief Contains implementation and description of the algorithm used to compute +/// global histogram equalization of input images. +/// +/// Algorithm :- +/// 1. If histogram A is to be equalized compute the cumulative histogram of A. +/// 2. Let CFD(A) refer to the cumulative histogram of A +/// 3. For a uniform histogram A', CDF(A') = A' +/// 4. We need to transfrom A to A' such that +/// 5. CDF(A') = CDF(A) => A' = CDF(A) +/// 6. Hence the pixel transform , px => histogram_of_ith_channel[px]. +/// + +/// \fn histogram_equalization +/// \ingroup HE +/// \tparam SrcKeyType Key Type of input histogram +/// @param src_hist INPUT Input source histogram +/// \brief Overload for histogram equalization algorithm, takes in a single source histogram +/// and returns the color map used for histogram equalization. +/// +template +std::map histogram_equalization(histogram const& src_hist) +{ + histogram dst_hist; + return histogram_equalization(src_hist, dst_hist); +} + +/// \overload histogram_equalization +/// \ingroup HE +/// \tparam SrcKeyType Key Type of input histogram +/// \tparam DstKeyType Key Type of output histogram +/// @param src_hist INPUT source histogram +/// @param dst_hist OUTPUT Output histogram +/// \brief Overload for histogram equalization algorithm, takes in both source histogram & +/// destination histogram and returns the color map used for histogram equalization +/// as well as transforming the destination histogram. +/// +template +std::map + histogram_equalization(histogram const& src_hist, histogram& dst_hist) +{ + static_assert( + std::is_integral::value && + std::is_integral::value, + "Source and destination histogram types are not appropriate"); + + using value_t = typename histogram::value_type; + dst_hist.clear(); + double sum = src_hist.sum(); + SrcKeyType min_key = std::numeric_limits::min(); + SrcKeyType max_key = std::numeric_limits::max(); + auto cumltv_srchist = cumulative_histogram(src_hist); + std::map color_map; + std::for_each(cumltv_srchist.begin(), cumltv_srchist.end(), [&](value_t const& v) { + DstKeyType trnsfrmd_key = + static_cast((v.second * (max_key - min_key)) / sum + min_key); + color_map[std::get<0>(v.first)] = trnsfrmd_key; + }); + std::for_each(src_hist.begin(), src_hist.end(), [&](value_t const& v) { + dst_hist[color_map[std::get<0>(v.first)]] += v.second; + }); + return color_map; +} + +/// \overload histogram_equalization +/// \ingroup HE +/// @param src_view INPUT source image view +/// @param dst_view OUTPUT Output image view +/// @param bin_width INPUT Histogram bin width +/// @param mask INPUT Specify is mask is to be used +/// @param src_mask INPUT Mask vector over input image +/// \brief Overload for histogram equalization algorithm, takes in both source & destination +/// image views and histogram equalizes the input image. +/// +template +void histogram_equalization( + SrcView const& src_view, + DstView const& dst_view, + std::size_t bin_width = 1, + bool mask = false, + std::vector> src_mask = {}) +{ + 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 same color space"); + + // Defining channel type + using source_channel_t = typename channel_type::type; + using dst_channel_t = typename channel_type::type; + using coord_t = typename SrcView::x_coord_t; + + std::size_t const channels = num_channels::value; + coord_t const width = src_view.width(); + coord_t const height = src_view.height(); + std::size_t pixel_max = std::numeric_limits::max(); + std::size_t pixel_min = std::numeric_limits::min(); + + for (std::size_t i = 0; i < channels; i++) + { + histogram h; + fill_histogram(nth_channel_view(src_view, i), h, bin_width, false, false, mask, src_mask); + h.normalize(); + auto h2 = cumulative_histogram(h); + for (std::ptrdiff_t src_y = 0; src_y < height; ++src_y) + { + auto src_it = nth_channel_view(src_view, i).row_begin(src_y); + auto dst_it = nth_channel_view(dst_view, i).row_begin(src_y); + for (std::ptrdiff_t src_x = 0; src_x < width; ++src_x) + { + if (mask && !src_mask[src_y][src_x]) + dst_it[src_x][0] = channel_convert(src_it[src_x][0]); + else + dst_it[src_x][0] = static_cast( + h2[src_it[src_x][0]] * (pixel_max - pixel_min) + pixel_min); + } + } + } +} + +}} //namespace boost::gil + +#endif diff --git a/test/core/image_processing/histogram_equalization.cpp b/test/core/image_processing/histogram_equalization.cpp new file mode 100644 index 0000000000..8aab634a03 --- /dev/null +++ b/test/core/image_processing/histogram_equalization.cpp @@ -0,0 +1,280 @@ +// +// Copyright 2020 Debabrata Mandal +// +// 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 +#include + +#include + +#include + +const int a = 5; +const double epsilon = 0.005; // Decided by the value 1/255 i.e. an error of 1 px in 255 px +boost::gil::gray8_image_t original(a, a); +boost::gil::gray8_image_t processed_1(a, a), processed_2(a, a), expected(a, a); +std::vector > test1_random{ + { 1, 10, 10, 10, 10}, + { 20, 25, 25, 55, 20}, + { 0, 55, 55, 55, 20}, + { 20, 255, 255, 255, 0}, + { 100, 100, 100, 10, 0}}; +std::vector > expected_test1{ + { 40, 91, 91, 91, 91}, + { 132, 153, 153, 193, 132}, + { 30, 193, 193, 193, 132}, + { 132, 255, 255, 255, 30}, + { 224, 224, 224, 91, 30}}; + +std::vector > all_white{ + {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}}; + +std::vector > expected_all_white{ + {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}}; + +std::vector > binary_img{ + {0 , 0 , 0 , 0 , 0 }, + {255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255}}; + +std::vector > expected_binary_img{ + {51 , 51 , 51 , 51 , 51 }, + {255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255}}; + +std::vector > test2_uniform{ + { 0, 10, 20, 30, 40}, + { 50, 60, 70, 80, 90}, + { 100, 110, 120, 130, 140}, + { 150, 160, 170, 180, 190}, + { 200, 210, 220, 230, 240}}; +std::vector > expected_test2{ + { 10, 20, 30, 40, 51}, + { 61, 71, 81, 91, 102}, + { 112, 122, 132, 142, 153}, + { 163, 173, 183, 193, 204}, + { 214, 224, 234, 244, 255}}; + +std::vector > test3_2peaks{ + { 0, 0, 0, 0, 10}, + { 40, 43, 44, 46, 50}, + { 55, 56, 44, 46, 44}, + { 200, 201, 202, 203, 200}, + { 201, 202, 201, 201, 22}}; +std::vector > expected_test3{ + { 40, 40, 40, 40, 51}, + { 71, 81, 112, 132, 142}, + { 153, 163, 112, 132, 112}, + { 183, 224, 244, 255, 183}, + { 224, 244, 224, 224, 61}}; + +std::vector > test_mask{ + {1, 10, 10, 10, 10}, + {20, 25, 25, 25, 20}, + {0, 25, 25, 25, 20}, + {20, 25, 25, 25, 0}, + {100, 100, 100, 10, 0}}; +std::vector > expected_test_mask{ + {1, 10, 10, 10, 10}, + {20, 255, 255, 255, 20}, + {0, 255, 255, 255, 20}, + {20, 255, 255, 255, 0}, + {100, 100, 100, 10, 0}}; + +std::vector > mask{ + {0, 0, 0, 0, 0}, + {0, 1, 1, 1, 0}, + {0, 1, 1, 1, 0}, + {0, 1, 1, 1, 0}, + {0, 0, 0, 0, 0}}; + +void vector_to_gray_image(boost::gil::gray8_image_t& img, + std::vector >& grid) +{ + for(std::ptrdiff_t y=0; y +bool equal_pixels(SrcView const& v1, SrcView const& v2, double threshold) +{ + double sum=0.0; + using pixel_t = typename boost::gil::get_pixel_type::type; + using channel_t = typename boost::gil::channel_type::type; + channel_t max_p = std::numeric_limits::max(); + channel_t min_p = std::numeric_limits::min(); + long int num_pixels = v1.width() * v1.height(); + std::size_t num_channels = boost::gil::num_channels::value; + for (std::ptrdiff_t y = 0; y < v1.height(); ++y) + { + auto it1 = v1.row_begin(y); + auto it2 = v2.row_begin(y); + for (std::ptrdiff_t x = 0; x < v2.width(); ++x) + { + for(std::ptrdiff_t c = 0; c < num_channels; ++c) + { + sum += abs(it1[x][c]-it2[x][c]); + } + } + } + return ( abs(sum) / (num_pixels * num_channels * (max_p - min_p)) < threshold ); +} + +void test_random_image() +{ + vector_to_gray_image(original,test1_random); + vector_to_gray_image(expected,expected_test1); + + histogram_equalization(boost::gil::const_view(original),boost::gil::view(processed_1)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(expected), epsilon)); + + // Process image again to look for differences + histogram_equalization(boost::gil::const_view(processed_1),boost::gil::view(processed_2)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(processed_2), epsilon)); + + // Test overloaded version + boost::gil::histogram hist, process_1, process_2; + fill_histogram(boost::gil::const_view(original), hist, 1, false, false); + histogram_equalization(hist, process_1); + + histogram_equalization(process_1, process_2); + BOOST_TEST(process_1.equals(process_2)); +} + +void test_random_image_with_mask() +{ + vector_to_gray_image(original,test_mask); + vector_to_gray_image(expected,expected_test_mask); + histogram_equalization(boost::gil::const_view(original),boost::gil::view(processed_1), 1, true, mask); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(expected), epsilon)); + + // Process image again to look for differences + histogram_equalization(boost::gil::const_view(processed_1),boost::gil::view(processed_2), 1, true, mask); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(processed_2), epsilon)); + + // Test overloaded version + boost::gil::histogram hist, process_1, process_2; + fill_histogram(boost::gil::const_view(original), hist, 1, false, false); + histogram_equalization(hist, process_1); + + histogram_equalization(process_1, process_2); + BOOST_TEST(process_1.equals(process_2)); +} + +void test_uniform_image() +{ + vector_to_gray_image(original,test2_uniform); + vector_to_gray_image(expected,expected_test2); + histogram_equalization(boost::gil::const_view(original),boost::gil::view(processed_1)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(expected), epsilon)); + + // Process image again to look for differences + histogram_equalization(boost::gil::const_view(processed_1),boost::gil::view(processed_2)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(processed_2), epsilon)); + + // Test overloaded version + boost::gil::histogram hist, process_1, process_2; + fill_histogram(boost::gil::const_view(original), hist, 1, false, false); + histogram_equalization(hist, process_1); + + histogram_equalization(process_1, process_2); + BOOST_TEST(process_1.equals(process_2)); +} + +void test_all_white_image() +{ + vector_to_gray_image(original,all_white); + vector_to_gray_image(expected,expected_all_white); + histogram_equalization(boost::gil::const_view(original),boost::gil::view(processed_1)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(expected), epsilon)); + + // Process image again to look for differences + histogram_equalization(boost::gil::const_view(processed_1),boost::gil::view(processed_2)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(processed_2), epsilon)); + + // Test overloaded version + boost::gil::histogram hist, process_1, process_2; + fill_histogram(boost::gil::const_view(original), hist, 1, false, false); + histogram_equalization(hist, process_1); + + histogram_equalization(process_1, process_2); + BOOST_TEST(process_1.equals(process_2)); +} + +void test_binary_image() +{ + vector_to_gray_image(original,binary_img); + vector_to_gray_image(expected,expected_binary_img); + histogram_equalization(boost::gil::const_view(original),boost::gil::view(processed_1)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(expected), epsilon)); + + // Process image again to look for differences + histogram_equalization(boost::gil::const_view(processed_1),boost::gil::view(processed_2)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(processed_2), epsilon)); + + // Test overloaded version + boost::gil::histogram hist, process_1, process_2; + fill_histogram(boost::gil::const_view(original), hist, 1, false, false); + histogram_equalization(hist, process_1); + + histogram_equalization(process_1, process_2); + BOOST_TEST(process_1.equals(process_2)); +} + +void test_double_peaked_image() +{ + vector_to_gray_image(original,test3_2peaks); + vector_to_gray_image(expected,expected_test3); + histogram_equalization(boost::gil::const_view(original),boost::gil::view(processed_1)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(expected), epsilon)); + + // Process image again to look for differences + histogram_equalization(boost::gil::const_view(processed_1),boost::gil::view(processed_2)); + BOOST_TEST(equal_pixels(boost::gil::view(processed_1), boost::gil::view(processed_2), epsilon)); + + // Test overloaded version + boost::gil::histogram hist, process_1, process_2; + fill_histogram(boost::gil::const_view(original), hist, 1, false, false); + histogram_equalization(hist, process_1); + + histogram_equalization(process_1, process_2); + BOOST_TEST(process_1.equals(process_2)); +} + +int main() +{ + //Basic tests for grayscale histogram_equalization + test_random_image(); + test_random_image_with_mask(); + test_all_white_image(); + test_binary_image(); + test_uniform_image(); + test_double_peaked_image(); + + return boost::report_errors(); +} From 77255e9e61b517ebfceb4f9439a074e61b4ba1c5 Mon Sep 17 00:00:00 2001 From: Debabrata Mandal <32168969+codejaeger@users.noreply.github.com> Date: Mon, 25 Jan 2021 02:03:38 +0530 Subject: [PATCH 017/193] Add histogram matching algorithm (#515) --- .../histogram_matching.rst | 87 ++++++++ .../contrast_enhancement/matching.jpg | Bin 0 -> 14950 bytes .../contrast_enhancement/matching_out.jpg | Bin 0 -> 7607 bytes example/histogram_matching.cpp | 45 ++++ .../image_processing/histogram_matching.hpp | 206 ++++++++++++++++++ .../image_processing/histogram_matching.cpp | 137 ++++++++++++ 6 files changed, 475 insertions(+) create mode 100644 doc/image_processing/contrast_enhancement/histogram_matching.rst create mode 100644 doc/image_processing/contrast_enhancement/matching.jpg create mode 100644 doc/image_processing/contrast_enhancement/matching_out.jpg create mode 100644 example/histogram_matching.cpp create mode 100644 include/boost/gil/image_processing/histogram_matching.hpp create mode 100644 test/core/image_processing/histogram_matching.cpp diff --git a/doc/image_processing/contrast_enhancement/histogram_matching.rst b/doc/image_processing/contrast_enhancement/histogram_matching.rst new file mode 100644 index 0000000000..be7883a0c9 --- /dev/null +++ b/doc/image_processing/contrast_enhancement/histogram_matching.rst @@ -0,0 +1,87 @@ +.. _hm: + +################## +Histogram Matching +################## + +Description +----------- + +Histogram Matching is a technique to match the histograms of two images. + +One use case of this would be when two images of the same location have been taken +under the same local illumination but with different sensors, bringing out different +features in either image. + +The famous histogram equalization is a special case of this algorithm when the reference image +is expected to have a uniform histogram. + + +Algorithm +--------- + +#. Calculate the histogram corresponding to input image and reference image. +#. If it is a multi channeled image (e.g. RGB), convert both to an independent color space + (like YCbCr, HSV etc.). +#. Then calculate the cumulative histogram over the input image and reference image. +#. Normalize both the histogram to bring bin values between 0-1. For multi-channeled images + normalize each channel independently (by the number of pixels in image). +#. If the cumulative histogram of input image is H(p\ :sub:`x`\) and of reference image is R(p\ :sub:`x'`\) + p\ :sub:`x`\ & p\ :sub:`x'`\ in [0, 255], then apply the transformation + p\ :sub:`x'`\ = R\ :sup:`-1`\ (H(p\ :sub:`x`\ )) + +**Explanation** + +Since we will be transforming the image to match a reference image, we match +the cumulative histogram of the image to the cumulative histogram of the reference histogram. + +Hence, + + => R(p\ :sub:`x'`\) = H(p\ :sub:`x`\ ) + + => p\ :sub:`x'`\ = R\ :sup:`-1`\ (H(p\ :sub:`x`\ )) + +Results +------- +The algorithm is applied on a few standard images. One of the transformations in shown below: + +**Original Image(left) & Reference Image(right)** + +.. figure:: matching.jpg + :width: 600px + :align: center + :height: 300px + :alt: Could not load image. + :figclass: align-center + +**Histogram matched Image** + +.. figure:: matching_out.jpg + :width: 300px + :align: center + :height: 300px + :alt: Could not load image. + :figclass: align-center + + +Demo +---- + +Usage Syntax: + + .. code-block:: cpp + + gray8_image_t inp_img, ref_img; + read_image("your_image.png", inp_img, png_tag{}); + read_image("your_ref_image.png", ref_img, png_tag{}); + gray8_image_t dst_img(inp_img.dimensions()); + histogram_matching(view(inp_img), view(ref_image), view(dst_img)); + + // To specify mask over input image + + vector> mask(inp_img.height(), vector(inp_img.width(), true)); + histogram_matching(view(inp_img), view(ref_image), view(dst_img), true, mask); + + .. tip:: Convert an RGB image to a channel independent color space + before trying the histogram matching algorithm. + diff --git a/doc/image_processing/contrast_enhancement/matching.jpg b/doc/image_processing/contrast_enhancement/matching.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d03e07a0404975ff535f89ccfc1757869ae83e56 GIT binary patch literal 14950 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 literal 0 HcmV?d00001 diff --git a/doc/image_processing/contrast_enhancement/matching_out.jpg b/doc/image_processing/contrast_enhancement/matching_out.jpg new file mode 100644 index 0000000000000000000000000000000000000000..869043accf89bd69a4a07fd75c81be676121295f GIT binary patch literal 7607 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 x#J|?b-aYJtUE!DC$9t6wf0rp-PDf7a-bm&q< +// +// 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 + +using namespace boost::gil; + +std::vector> get_mask(gray8_view_t const& mask) +{ + std::vector> mask_vec(mask.height(), std::vector(mask.width(), 0)); + for (std::size_t i = 0; i < mask.height(); i++) + { + for (std::size_t j = 0; j < mask.width(); j++) + { + mask_vec[i][j] = mask(j, i); + } + } + return mask_vec; +} + +int main() +{ + gray8_image_t img; + read_image("test_adaptive.png", img, png_tag{}); + + gray8_image_t ref_img; + read_image("test_adaptive.png", ref_img, png_tag{}); + + gray8_image_t img_out(img.dimensions()); + + boost::gil::histogram_matching(view(img), view(ref_img), view(img_out)); + + write_view("histogram_gray_matching.png", view(img_out), png_tag{}); + + return 0; +} diff --git a/include/boost/gil/image_processing/histogram_matching.hpp b/include/boost/gil/image_processing/histogram_matching.hpp new file mode 100644 index 0000000000..ea49a5c153 --- /dev/null +++ b/include/boost/gil/image_processing/histogram_matching.hpp @@ -0,0 +1,206 @@ +// +// Copyright 2020 Debabrata Mandal +// +// 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_HISTOGRAM_MATCHING_HPP +#define BOOST_GIL_IMAGE_PROCESSING_HISTOGRAM_MATCHING_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { namespace gil { + +///////////////////////////////////////// +/// Histogram Matching(HM) +///////////////////////////////////////// +/// \defgroup HM HM +/// \brief Contains implementation and description of the algorithm used to compute +/// global histogram matching of input images. +/// +/// Algorithm :- +/// 1. Calculate histogram A(pixel) of input image and G(pixel) of reference image. +/// 2. Compute the normalized cumulative(CDF) histograms of A and G. +/// 3. Match the histograms using transofrmation => CDF(A(px)) = CDF(G(px')) +/// => px' = Inv-CDF (CDF(px)) +/// + +/// \fn histogram_matching +/// \ingroup HM +/// \tparam SrcKeyType Key Type of input histogram +/// @param src_hist INPUT Input source histogram +/// @param ref_hist INPUT Input reference histogram +/// \brief Overload for histogram matching algorithm, takes in a single source histogram & +/// reference histogram and returns the color map used for histogram matching. +/// +template +std::map + histogram_matching(histogram const& src_hist, histogram const& ref_hist) +{ + histogram dst_hist; + return histogram_matching(src_hist, ref_hist, dst_hist); +} + +/// \overload histogram_matching +/// \ingroup HM +/// \tparam SrcKeyType Key Type of input histogram +/// \tparam RefKeyType Key Type of reference histogram +/// \tparam DstKeyType Key Type of output histogram +/// @param src_hist INPUT source histogram +/// @param ref_hist INPUT reference histogram +/// @param dst_hist OUTPUT Output histogram +/// \brief Overload for histogram matching algorithm, takes in source histogram, reference +/// histogram & destination histogram and returns the color map used for histogram +/// matching as well as transforming the destination histogram. +/// +template +std::map histogram_matching( + histogram const& src_hist, + histogram const& ref_hist, + histogram& dst_hist) +{ + static_assert( + std::is_integral::value && + std::is_integral::value && + std::is_integral::value, + "Source, Refernce or Destination histogram type is not appropriate."); + + using value_t = typename histogram::value_type; + dst_hist.clear(); + double src_sum = src_hist.sum(); + double ref_sum = ref_hist.sum(); + auto cumltv_srchist = cumulative_histogram(src_hist); + auto cumltv_refhist = cumulative_histogram(ref_hist); + std::map inverse_mapping; + + std::vector::key_type> src_keys, ref_keys; + src_keys = src_hist.sorted_keys(); + ref_keys = ref_hist.sorted_keys(); + std::ptrdiff_t start = ref_keys.size() - 1; + RefKeyType ref_max; + if (start >= 0) + ref_max = std::get<0>(ref_keys[start]); + + for (std::ptrdiff_t j = src_keys.size() - 1; j >= 0; --j) + { + double src_val = (cumltv_srchist[src_keys[j]] * ref_sum) / src_sum; + while (cumltv_refhist[ref_keys[start]] > src_val && start > 0) + { + start--; + } + if (abs(cumltv_refhist[ref_keys[start]] - src_val) > + abs(cumltv_refhist(std::min(ref_max, std::get<0>(ref_keys[start + 1]))) - + src_val)) + { + inverse_mapping[std::get<0>(src_keys[j])] = + std::min(ref_max, std::get<0>(ref_keys[start + 1])); + } + else + { + inverse_mapping[std::get<0>(src_keys[j])] = std::get<0>(ref_keys[start]); + } + if (j == 0) + break; + } + std::for_each(src_hist.begin(), src_hist.end(), [&](value_t const& v) { + dst_hist[inverse_mapping[std::get<0>(v.first)]] += v.second; + }); + return inverse_mapping; +} + +/// \overload histogram_matching +/// \ingroup HM +/// @param src_view INPUT source image view +/// @param ref_view INPUT Reference image view +/// @param dst_view OUTPUT Output image view +/// @param bin_width INPUT Histogram bin width +/// @param mask INPUT Specify is mask is to be used +/// @param src_mask INPUT Mask vector over input image +/// @param ref_mask INPUT Mask vector over reference image +/// \brief Overload for histogram matching algorithm, takes in both source, reference & +/// destination image views and histogram matches the input image using the +/// reference image. +/// +template +void histogram_matching( + SrcView const& src_view, + ReferenceView const& ref_view, + DstView const& dst_view, + std::size_t bin_width = 1, + bool mask = false, + std::vector> src_mask = {}, + std::vector> ref_mask = {}) +{ + gil_function_requires>(); + 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 reference view must have same color space"); + + static_assert( + color_spaces_are_compatible< + typename color_space_type::type, + typename color_space_type::type>::value, + "Source and destination view must have same color space"); + + // Defining channel type + using source_channel_t = typename channel_type::type; + using ref_channel_t = typename channel_type::type; + using dst_channel_t = typename channel_type::type; + using coord_t = typename SrcView::x_coord_t; + + std::size_t const channels = num_channels::value; + coord_t const width = src_view.width(); + coord_t const height = src_view.height(); + source_channel_t src_pixel_min = std::numeric_limits::min(); + source_channel_t src_pixel_max = std::numeric_limits::max(); + ref_channel_t ref_pixel_min = std::numeric_limits::min(); + ref_channel_t ref_pixel_max = std::numeric_limits::max(); + dst_channel_t dst_pixel_min = std::numeric_limits::min(); + dst_channel_t dst_pixel_max = std::numeric_limits::max(); + + for (std::size_t i = 0; i < channels; i++) + { + histogram src_histogram; + histogram ref_histogram; + fill_histogram( + nth_channel_view(src_view, i), src_histogram, bin_width, false, false, mask, src_mask, + std::tuple(src_pixel_min), + std::tuple(src_pixel_max), true); + fill_histogram( + nth_channel_view(ref_view, i), ref_histogram, bin_width, false, false, mask, ref_mask, + std::tuple(ref_pixel_min), std::tuple(ref_pixel_max), + true); + auto inverse_mapping = histogram_matching(src_histogram, ref_histogram); + for (std::ptrdiff_t src_y = 0; src_y < height; ++src_y) + { + auto src_it = nth_channel_view(src_view, i).row_begin(src_y); + auto dst_it = nth_channel_view(dst_view, i).row_begin(src_y); + for (std::ptrdiff_t src_x = 0; src_x < width; ++src_x) + { + if (mask && !src_mask[src_y][src_x]) + dst_it[src_x][0] = src_it[src_x][0]; + else + dst_it[src_x][0] = + static_cast(inverse_mapping[src_it[src_x][0]]); + } + } + } +} + +}} //namespace boost::gil + +#endif diff --git a/test/core/image_processing/histogram_matching.cpp b/test/core/image_processing/histogram_matching.cpp new file mode 100644 index 0000000000..56672e0494 --- /dev/null +++ b/test/core/image_processing/histogram_matching.cpp @@ -0,0 +1,137 @@ +// +// Copyright 2020 Debabrata Mandal +// +// 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 +#include + +#include +#include + +const int a = 5; +const double epsilon = 0.000001; // Decided by the value 5/255 i.e. an error of 5 px in 255 px +boost::gil::gray8_image_t original(a, a), reference(a, a); +boost::gil::gray8_image_t processed(a, a), processed2(a, a); +std::vector > test1_random{ + { 1, 10, 10, 10, 10}, + { 20, 25, 25, 55, 20}, + { 0, 55, 55, 55, 20}, + { 20, 255, 255, 255, 0}, + { 100, 100, 100, 10, 0}}; +std::vector > test1_reference{ + { 0, 10, 20, 30, 40}, + { 50, 60, 70, 80, 90}, + { 100, 110, 120, 130, 140}, + { 150, 160, 170, 180, 190}, + { 200, 210, 220, 230, 240}}; + +std::vector > test2_uniform{ + { 0, 10, 20, 30, 40}, + { 50, 60, 70, 80, 90}, + { 100, 110, 120, 130, 140}, + { 150, 160, 170, 180, 190}, + { 200, 210, 220, 230, 240}}; +std::vector > test2_reference{ + { 10, 20, 30, 40, 51}, + { 61, 71, 81, 91, 102}, + { 112, 122, 132, 142, 153}, + { 163, 173, 183, 193, 204}, + { 214, 224, 234, 244, 255}}; + +std::vector > test3_equal_image{ + { 0, 10, 20, 30, 40}, + { 50, 60, 70, 80, 90}, + { 100, 110, 120, 130, 140}, + { 150, 160, 170, 180, 190}, + { 200, 210, 220, 230, 240}}; + +std::vector > test3_reference{ + { 0, 10, 20, 30, 40}, + { 50, 60, 70, 80, 90}, + { 100, 110, 120, 130, 140}, + { 150, 160, 170, 180, 190}, + { 200, 210, 220, 230, 240}}; + +void vector_to_gray_image(boost::gil::gray8_image_t& img, + std::vector >& grid) +{ + for(std::ptrdiff_t y=0; y +bool equal_histograms(SrcView const& v1, SrcView const& v2, double threshold = epsilon) +{ + double sum=0.0; + using channel_t = typename boost::gil::channel_type::type; + boost::gil::histogram h1, h2; + using value_t = typename boost::gil::histogram::value_type; + channel_t max_p = std::numeric_limits::max(); + channel_t min_p = std::numeric_limits::min(); + long int num_pixels = v1.width() * v1.height(); + + boost::gil::fill_histogram(v1, h1, 1, false, false); + boost::gil::fill_histogram(v2, h2, 1, false, false); + auto ch1 = boost::gil::cumulative_histogram(h1); + auto ch2 = boost::gil::cumulative_histogram(h2); + std::for_each(ch1.begin(), ch1.end(), [&](value_t const& v) { + sum+=abs(v.second-ch1[v.first]); + }); + return ( abs(sum) / (ch1.size() * (max_p - min_p)) < threshold ); +} + +void test_random_image() +{ + vector_to_gray_image(original,test1_random); + vector_to_gray_image(reference,test1_reference); + histogram_matching(boost::gil::const_view(original),boost::gil::const_view(reference),boost::gil::view(processed)); + BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(reference))); + + histogram_matching(boost::gil::const_view(processed),boost::gil::const_view(reference),boost::gil::view(processed2)); + BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(processed2))); +} + +void test_uniform_image() +{ + vector_to_gray_image(original,test2_uniform); + vector_to_gray_image(reference,test2_reference); + histogram_matching(boost::gil::const_view(original),boost::gil::const_view(reference),boost::gil::view(processed)); + BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(reference))); + + histogram_matching(boost::gil::const_view(processed),boost::gil::const_view(reference),boost::gil::view(processed2)); + BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(processed2))); +} + +void test_equal_image() +{ + vector_to_gray_image(original,test3_equal_image); + vector_to_gray_image(reference,test3_reference); + histogram_matching(boost::gil::const_view(original),boost::gil::const_view(reference),boost::gil::view(processed)); + BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(reference))); + + histogram_matching(boost::gil::const_view(processed),boost::gil::const_view(reference),boost::gil::view(processed2)); + BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(processed2))); +} + +int main() +{ + //Basic tests for grayscale histogram_equalization + test_random_image(); + test_uniform_image(); + test_equal_image(); + + return boost::report_errors(); +} From b82aed8f8e12602e1a3537ebbdbeb6a0b357d481 Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Mon, 25 Jan 2021 02:43:06 +0600 Subject: [PATCH 018/193] Implement hstack and vstack (#506) Allow non-equal dims along stack dim Width can be different in hstack, height can be different in vstack --- example/harris.cpp | 148 ++++++++++++++++++++------------------------ example/hvstack.hpp | 116 ++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 80 deletions(-) create mode 100644 example/hvstack.hpp diff --git a/example/harris.cpp b/example/harris.cpp index f860105d2f..81233fe38a 100644 --- a/example/harris.cpp +++ b/example/harris.cpp @@ -5,17 +5,21 @@ // 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 #include -#include +#include +#include +#include +#include +#include + +#include "hvstack.hpp" + +#include #include -#include #include -#include +#include +#include namespace gil = boost::gil; @@ -29,24 +33,28 @@ gil::gray8_image_t to_grayscale(gil::rgb8_view_t original) gil::gray8_image_t output_image(original.dimensions()); auto output = gil::view(output_image); constexpr double max_channel_intensity = (std::numeric_limits::max)(); - for (long int y = 0; y < original.height(); ++y) { - for (long int x = 0; x < original.width(); ++x) { + for (long int y = 0; y < original.height(); ++y) + { + for (long int x = 0; x < original.width(); ++x) + { // scale the values into range [0, 1] and calculate linear intensity - double red_intensity = original(x, y).at(std::integral_constant{}) - / max_channel_intensity; - double green_intensity = original(x, y).at(std::integral_constant{}) - / max_channel_intensity; - double blue_intensity = original(x, y).at(std::integral_constant{}) - / max_channel_intensity; - auto linear_luminosity = 0.2126 * red_intensity - + 0.7152 * green_intensity - + 0.0722 * blue_intensity; + double red_intensity = + original(x, y).at(std::integral_constant{}) / max_channel_intensity; + double green_intensity = + original(x, y).at(std::integral_constant{}) / max_channel_intensity; + double blue_intensity = + original(x, y).at(std::integral_constant{}) / max_channel_intensity; + auto linear_luminosity = + 0.2126 * red_intensity + 0.7152 * green_intensity + 0.0722 * blue_intensity; // perform gamma adjustment double gamma_compressed_luminosity = 0; - if (linear_luminosity < 0.0031308) { + if (linear_luminosity < 0.0031308) + { gamma_compressed_luminosity = linear_luminosity * 12.92; - } else { + } + else + { gamma_compressed_luminosity = 1.055 * std::pow(linear_luminosity, 1 / 2.4) - 0.055; } @@ -62,45 +70,43 @@ void apply_gaussian_blur(gil::gray8_view_t input_view, gil::gray8_view_t output_ { constexpr static auto filterHeight = 5ull; constexpr static auto filterWidth = 5ull; - constexpr static double filter[filterHeight][filterWidth] = - { - 2, 4, 6, 4, 2, - 4, 9, 12, 9, 4, - 5, 12, 15, 12, 5, - 4, 9, 12, 9, 4, - 2, 4, 5, 4, 2, + constexpr static double filter[filterHeight][filterWidth] = { + 2, 4, 6, 4, 2, 4, 9, 12, 9, 4, 5, 12, 15, 12, 5, 4, 9, 12, 9, 4, 2, 4, 5, 4, 2, }; constexpr double factor = 1.0 / 159; constexpr double bias = 0.0; const auto height = input_view.height(); const auto width = input_view.width(); - for (long x = 0; x < width; ++x) { - for (long y = 0; y < height; ++y) { + for (long x = 0; x < width; ++x) + { + for (long y = 0; y < height; ++y) + { double intensity = 0.0; - for (size_t filter_y = 0; filter_y < filterHeight; ++filter_y) { - for (size_t filter_x = 0; filter_x < filterWidth; ++filter_x) { + for (size_t filter_y = 0; filter_y < filterHeight; ++filter_y) + { + for (size_t filter_x = 0; filter_x < filterWidth; ++filter_x) + { int image_x = x - filterWidth / 2 + filter_x; int image_y = y - filterHeight / 2 + filter_y; - if (image_x >= input_view.width() || image_x < 0 - || image_y >= input_view.height() || image_y < 0) { + if (image_x >= input_view.width() || image_x < 0 || + image_y >= input_view.height() || image_y < 0) + { continue; } auto& pixel = input_view(image_x, image_y); - intensity += pixel.at(std::integral_constant{}) - * filter[filter_y][filter_x]; + intensity += + pixel.at(std::integral_constant{}) * filter[filter_y][filter_x]; } } auto& pixel = output_view(gil::point_t(x, y)); pixel = (std::min)((std::max)(int(factor * intensity + bias), 0), 255); } - } } -std::vector suppress( - gil::gray32f_view_t harris_response, - double harris_response_threshold) +std::vector suppress(gil::gray32f_view_t harris_response, + double harris_response_threshold) { std::vector corner_points; for (gil::gray32f_view_t::coord_t y = 1; y < harris_response.height() - 1; ++y) @@ -111,29 +117,17 @@ std::vector suppress( return pixel.at(std::integral_constant{}); }; double values[9] = { - value(harris_response(x - 1, y - 1)), - value(harris_response(x, y - 1)), - value(harris_response(x + 1, y - 1)), - value(harris_response(x - 1, y)), - value(harris_response(x, y)), - value(harris_response(x + 1, y)), - value(harris_response(x - 1, y + 1)), - value(harris_response(x, y + 1)), - value(harris_response(x + 1, y + 1)) - }; + value(harris_response(x - 1, y - 1)), value(harris_response(x, y - 1)), + value(harris_response(x + 1, y - 1)), value(harris_response(x - 1, y)), + value(harris_response(x, y)), value(harris_response(x + 1, y)), + value(harris_response(x - 1, y + 1)), value(harris_response(x, y + 1)), + value(harris_response(x + 1, y + 1))}; - auto maxima = *std::max_element( - values, - values + 9, - [](double lhs, double rhs) - { - return lhs < rhs; - } - ); + auto maxima = *std::max_element(values, values + 9, + [](double lhs, double rhs) { return lhs < rhs; }); - if (maxima == value(harris_response(x, y)) - && std::count(values, values + 9, maxima) == 1 - && maxima >= harris_response_threshold) + if (maxima == value(harris_response(x, y)) && + std::count(values, values + 9, maxima) == 1 && maxima >= harris_response_threshold) { corner_points.emplace_back(x, y); } @@ -147,8 +141,9 @@ int main(int argc, char* argv[]) { if (argc != 6) { - std::cout << "usage: " << argv[0] << " " - " \n"; + std::cout << "usage: " << argv[0] + << " " + " \n"; return -1; } @@ -159,7 +154,8 @@ int main(int argc, char* argv[]) gil::rgb8_image_t input_image; gil::read_image(argv[1], input_image, gil::png_tag{}); - + auto original_image = input_image; + auto original_view = gil::view(original_image); auto input_view = gil::view(input_image); auto grayscaled = to_grayscale(input_view); gil::gray8_image_t smoothed_image(grayscaled.dimensions()); @@ -178,30 +174,22 @@ int main(int argc, char* argv[]) gil::gray32f_image_t m11(x_gradient.dimensions()); gil::gray32f_image_t m12_21(x_gradient.dimensions()); gil::gray32f_image_t m22(x_gradient.dimensions()); - gil::compute_tensor_entries( - x_gradient, - y_gradient, - gil::view(m11), - gil::view(m12_21), - gil::view(m22) - ); + gil::compute_tensor_entries(x_gradient, y_gradient, gil::view(m11), gil::view(m12_21), + gil::view(m22)); gil::gray32f_image_t harris_response(x_gradient.dimensions()); auto gaussian_kernel = gil::generate_gaussian_kernel(window_size, 0.84089642); - gil::compute_harris_responses( - gil::view(m11), - gil::view(m12_21), - gil::view(m22), - gaussian_kernel, - discrimnation_constant, - gil::view(harris_response) - ); + gil::compute_harris_responses(gil::view(m11), gil::view(m12_21), gil::view(m22), + gaussian_kernel, discrimnation_constant, + gil::view(harris_response)); auto corner_points = suppress(gil::view(harris_response), harris_response_threshold); - for (auto point: corner_points) + for (auto point : corner_points) { input_view(point) = gil::rgb8_pixel_t(0, 0, 0); input_view(point).at(std::integral_constant{}) = 255; } - gil::write_view(argv[5], input_view, gil::png_tag{}); + auto stacked = gil::hstack(std::vector{original_view, input_view}); + + gil::write_view(argv[5], gil::view(stacked), gil::png_tag{}); } diff --git a/example/hvstack.hpp b/example/hvstack.hpp new file mode 100644 index 0000000000..1b4daaaf13 --- /dev/null +++ b/example/hvstack.hpp @@ -0,0 +1,116 @@ +#include "boost/gil/image_view_factory.hpp" +#include +#include +#include +#include +#include + +namespace boost { namespace gil { +template +void hstack(const std::vector& views, const View& output_view) +{ + if (views.size() == 0) + { + throw std::invalid_argument("empty views vector is passed - cannot create stacked image"); + } + + auto height = views.front().height(); + auto width = views.front().width(); + for (const auto& view : views) + { + if (view.height() != height) + { + throw std::invalid_argument("one or many views are not of the same height"); + } + } + + std::ptrdiff_t full_width = + std::accumulate(views.begin(), views.end(), 0, + [](std::ptrdiff_t old, const View& view) { return old + view.width(); }); + if (output_view.width() != full_width || output_view.height() != height) + { + throw std::invalid_argument("the destination view is not of the right dimensions"); + } + + std::ptrdiff_t current_x = 0; + for (std::size_t i = 0; i < views.size(); ++i) + { + auto subview = + subimage_view(output_view, current_x, 0, views[i].width(), views[i].height()); + copy_pixels(views[i], subview); + current_x += views[i].width(); + } +} + +template +image hstack(const std::vector& views) +{ + if (views.size() == 0) + { + throw std::invalid_argument("empty views vector is passed - cannot create stacked image"); + } + + auto dimensions = views.front().dimensions(); + std::ptrdiff_t full_width = + std::accumulate(views.begin(), views.end(), 0, + [](std::ptrdiff_t old, const View& view) { return old + view.width(); }); + std::ptrdiff_t height = views.front().height(); + image result_image(full_width, height); + hstack(views, view(result_image)); + return result_image; +} + +template +void vstack(const std::vector& views, const View& output_view) +{ + if (views.size() == 0) + { + throw std::invalid_argument("empty views vector is passed - cannot create stacked image"); + } + + auto full_height = + std::accumulate(views.begin(), views.end(), 0, + [](std::ptrdiff_t old, const View& view) { return old + view.height(); }); + std::ptrdiff_t width = views.front().height(); + + for (const auto& view : views) + { + if (view.width() != width) + { + throw std::invalid_argument("one or many views are not of the same width"); + } + } + + if (output_view != full_height || output_view.width() != width) + { + throw std::invalid_argument("the destination view is not of the right dimensions"); + } + + std::ptrdiff_t current_y = 0; + for (std::size_t i = 0; i < views.size(); ++i) + { + auto subview = + subimage_view(output_view, 0, current_y, views[i].width(), views[i].height()); + copy_pixels(views[i], subview); + current_y += views[i].height(); + } +} + +template +image vstack(const std::vector& views) +{ + if (views.size() == 0) + { + throw std::invalid_argument("empty views vector is passed - cannot create stacked image"); + } + + auto full_height = + std::accumulate(views.begin(), views.end(), 0, + [](std::ptrdiff_t old, const View& view) { return old + view.height(); }); + std::ptrdiff_t width = views.front().height(); + + image result_image(width, full_height); + hstack(views, view(result_image)); + return result_image; +} +}} // namespace boost::gil From a68a95d5f457038d4851c900e57d430dfb09eca8 Mon Sep 17 00:00:00 2001 From: Debabrata Mandal <32168969+codejaeger@users.noreply.github.com> Date: Mon, 25 Jan 2021 03:52:48 +0530 Subject: [PATCH 019/193] Add code for ahe algorithm (#516) --- example/adaptive_he.cpp | 25 ++ .../adaptive_histogram_equalization.hpp | 307 ++++++++++++++++++ test/core/image_processing/adaptive_he.cpp | 113 +++++++ 3 files changed, 445 insertions(+) create mode 100644 example/adaptive_he.cpp create mode 100644 include/boost/gil/image_processing/adaptive_histogram_equalization.hpp create mode 100644 test/core/image_processing/adaptive_he.cpp diff --git a/example/adaptive_he.cpp b/example/adaptive_he.cpp new file mode 100644 index 0000000000..0610932c98 --- /dev/null +++ b/example/adaptive_he.cpp @@ -0,0 +1,25 @@ +// +// Copyright 2020 Debabrata Mandal +// +// 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 + +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()); + + boost::gil::non_overlapping_interpolated_clahe(view(img), view(img_out)); + write_view("out-adaptive.png", view(img_out), png_tag{}); + return 0; +} diff --git a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp new file mode 100644 index 0000000000..9dcbfba928 --- /dev/null +++ b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp @@ -0,0 +1,307 @@ +// +// Copyright 2020 Debabrata Mandal +// +// 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_ADAPTIVE_HISTOGRAM_EQUALIZATION_HPP +#define BOOST_GIL_IMAGE_PROCESSING_ADAPTIVE_HISTOGRAM_EQUALIZATION_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { namespace gil { + +///////////////////////////////////////// +/// Adaptive Histogram Equalization(AHE) +///////////////////////////////////////// +/// \defgroup AHE AHE +/// \brief Contains implementation and description of the algorithm used to compute +/// adaptive histogram equalization of input images. Naming for the AHE functions +/// are done in the following way +/// __.._ahe +/// For example, for AHE done using local (non-overlapping) tiles/blocks and +/// final output interpolated among tiles , it is called +/// non_overlapping_interpolated_clahe +/// + +namespace detail { + +/// \defgroup AHE-helpers AHE-helpers +/// \brief AHE helper functions + +/// \fn double actual_clip_limit +/// \ingroup AHE-helpers +/// \brief Computes the actual clip limit given a clip limit value using binary search. +/// Reference - Adaptive Histogram Equalization and Its Variations +/// (http://www.cs.unc.edu/techreports/86-013.pdf, Pg - 15) +/// +template +double actual_clip_limit(SrcHist const& src_hist, double cliplimit = 0.03) +{ + double epsilon = 1.0; + using value_t = typename SrcHist::value_type; + double sum = src_hist.sum(); + std::size_t num_bins = src_hist.size(); + + cliplimit = sum * cliplimit; + long low = 0, high = cliplimit, middle = low; + while (high - low >= 1) + { + middle = (low + high + 1) >> 1; + long excess = 0; + std::for_each(src_hist.begin(), src_hist.end(), [&](value_t const& v) { + if (v.second > middle) + excess += v.second - middle; + }); + if (abs(excess - (cliplimit - middle) * num_bins) < epsilon) + break; + else if (excess > (cliplimit - middle) * num_bins) + high = middle - 1; + else + low = middle + 1; + } + return middle / sum; +} + +/// \fn void clip_and_redistribute +/// \ingroup AHE-helpers +/// \brief Clips and redistributes excess pixels based on the actual clip limit value +/// obtained from the other helper function actual_clip_limit +/// Reference - Graphic Gems 4, Pg. 474 +/// (http://cas.xav.free.fr/Graphics%20Gems%204%20-%20Paul%20S.%20Heckbert.pdf) +/// +template +void clip_and_redistribute(SrcHist const& src_hist, DstHist& dst_hist, double clip_limit = 0.03) +{ + using value_t = typename SrcHist::value_type; + double sum = src_hist.sum(); + double actual_clip_value = detail::actual_clip_limit(src_hist, clip_limit); + // double actual_clip_value = clip_limit; + long actual_clip_limit = actual_clip_value * sum; + double excess = 0; + std::for_each(src_hist.begin(), src_hist.end(), [&](value_t const& v) { + if (v.second > actual_clip_limit) + excess += v.second - actual_clip_limit; + }); + std::for_each(src_hist.begin(), src_hist.end(), [&](value_t const& v) { + if (v.second >= actual_clip_limit) + dst_hist[dst_hist.key_from_tuple(v.first)] = clip_limit * sum; + else + dst_hist[dst_hist.key_from_tuple(v.first)] = v.second + excess / src_hist.size(); + }); + long rem = long(excess) % src_hist.size(); + if (rem == 0) + return; + long period = round(src_hist.size() / rem); + std::size_t index = 0; + while (rem) + { + if (dst_hist(index) >= clip_limit * sum) + { + index = (index + 1) % src_hist.size(); + } + dst_hist(index)++; + rem--; + index = (index + period) % src_hist.size(); + } +} + +} // namespace detail + + +/// \fn void non_overlapping_interpolated_clahe +/// \ingroup AHE +/// @param src_view Input Source image view +/// @param dst_view Output Output image view +/// @param tile_width_x Input Tile width along x-axis to apply HE +/// @param tile_width_y Input Tile width along x-axis to apply HE +/// @param clip_limit Input Clipping limit to be applied +/// @param bin_width Input Bin widths for histogram +/// @param mask Input Specify if mask is to be used +/// @param src_mask Input Mask on input image to ignore specified pixels +/// \brief Performs local histogram equalization on tiles of size (tile_width_x, tile_width_y) +/// Then uses the clip limit to redistribute excess pixels above the limit uniformly to +/// other bins. The clip limit is specified as a fraction i.e. a bin's value is clipped +/// if bin_value >= clip_limit * (Total number of pixels in the tile) +/// +template +void non_overlapping_interpolated_clahe( + SrcView const& src_view, + DstView const& dst_view, + std::size_t tile_width_x = 20, + std::size_t tile_width_y = 20, + double clip_limit = 0.03, + std::size_t bin_width = 1.0, + bool mask = false, + std::vector> src_mask = {}) +{ + 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 same color space"); + + using source_channel_t = typename channel_type::type; + using dst_channel_t = typename channel_type::type; + using coord_t = typename SrcView::x_coord_t; + + std::size_t const channels = num_channels::value; + coord_t const width = src_view.width(); + coord_t const height = src_view.height(); + std::size_t pixel_max = std::numeric_limits::max(); + std::size_t pixel_min = std::numeric_limits::min(); + + // Find control points + + std::vector sample_x; + coord_t sample_x1 = tile_width_x / 2; + coord_t sample_x2 = (tile_width_x + 1) / 2; + coord_t sample_y1 = tile_width_y / 2; + coord_t sample_y2 = (tile_width_y + 1) / 2; + + auto extend_left = tile_width_x; + auto extend_top = tile_width_y; + auto extend_right = (tile_width_x - width % tile_width_x) % tile_width_x + tile_width_x; + auto extend_bottom = (tile_width_y - height % tile_width_y) % tile_width_y + tile_width_y; + + auto new_width = width + extend_left + extend_right; + auto new_height = height + extend_top + extend_bottom; + + image padded_img(new_width, new_height); + + auto top_left_x = tile_width_x; + auto top_left_y = tile_width_y; + auto bottom_right_x = tile_width_x + width; + auto bottom_right_y = tile_width_y + height; + + copy_pixels(src_view, subimage_view(view(padded_img), top_left_x, top_left_y, width, height)); + + for (std::size_t k = 0; k < channels; k++) + { + std::vector> prev_row(new_width / tile_width_x), + next_row((new_width / tile_width_x)); + std::vector> prev_map( + new_width / tile_width_x), + next_map((new_width / tile_width_x)); + + coord_t prev = 0, next = 1; + auto channel_view = nth_channel_view(view(padded_img), k); + + for (std::ptrdiff_t i = top_left_y; i < bottom_right_y; ++i) + { + if ((i - sample_y1) / tile_width_y >= next || i == top_left_y) + { + if (i != top_left_y) + { + prev = next; + next++; + } + prev_row = next_row; + prev_map = next_map; + for (std::ptrdiff_t j = sample_x1; j < new_width; j += tile_width_x) + { + auto img_view = subimage_view( + channel_view, j - sample_x1, next * tile_width_y, + std::max( + std::min(tile_width_x + j - sample_x1, bottom_right_x) - + (j - sample_x1), + 0), + std::max( + std::min((next + 1) * tile_width_y, bottom_right_y) - + next * tile_width_y, + 0)); + + fill_histogram( + img_view, next_row[(j - sample_x1) / tile_width_x], bin_width, false, + false); + + detail::clip_and_redistribute( + next_row[(j - sample_x1) / tile_width_x], + next_row[(j - sample_x1) / tile_width_x], clip_limit); + + next_map[(j - sample_x1) / tile_width_x] = + histogram_equalization(next_row[(j - sample_x1) / tile_width_x]); + } + } + bool prev_row_mask = 1, next_row_mask = 1; + if (prev == 0) + prev_row_mask = false; + else if (next + 1 == new_height / tile_width_y) + next_row_mask = false; + for (std::ptrdiff_t j = top_left_x; j < bottom_right_x; ++j) + { + bool prev_col_mask = true, next_col_mask = true; + if ((j - sample_x1) / tile_width_x == 0) + prev_col_mask = false; + else if ((j - sample_x1) / tile_width_x + 1 == new_width / tile_width_x - 1) + next_col_mask = false; + + // Bilinear interpolation + point_t top_left( + (j - sample_x1) / tile_width_x * tile_width_x + sample_x1, + prev * tile_width_y + sample_y1); + point_t top_right(top_left.x + tile_width_x, top_left.y); + point_t bottom_left(top_left.x, top_left.y + tile_width_y); + point_t bottom_right(top_left.x + tile_width_x, top_left.y + tile_width_y); + + long double x_diff = top_right.x - top_left.x; + long double y_diff = bottom_left.y - top_left.y; + + long double x1 = (j - top_left.x) / x_diff; + long double x2 = (top_right.x - j) / x_diff; + long double y1 = (i - top_left.y) / y_diff; + long double y2 = (bottom_left.y - i) / y_diff; + + if (prev_row_mask == 0) + y1 = 1; + else if (next_row_mask == 0) + y2 = 1; + if (prev_col_mask == 0) + x1 = 1; + else if (next_col_mask == 0) + x2 = 1; + + long double numerator = + ((prev_row_mask & prev_col_mask) * x2 * + prev_map[(top_left.x - sample_x1) / tile_width_x][channel_view(j, i)] + + (prev_row_mask & next_col_mask) * x1 * + prev_map[(top_right.x - sample_x1) / tile_width_x][channel_view(j, i)]) * + y2 + + ((next_row_mask & prev_col_mask) * x2 * + next_map[(bottom_left.x - sample_x1) / tile_width_x][channel_view(j, i)] + + (next_row_mask & next_col_mask) * x1 * + next_map[(bottom_right.x - sample_x1) / tile_width_x][channel_view(j, i)]) * + y1; + + if (mask && !src_mask[i - top_left_y][j - top_left_x]) + { + dst_view(j - top_left_x, i - top_left_y) = + channel_convert( + static_cast(channel_view(i, j))); + } + else + { + dst_view(j - top_left_x, i - top_left_y) = + channel_convert(static_cast(numerator)); + } + } + } + } +} + +}} //namespace boost::gil + +#endif diff --git a/test/core/image_processing/adaptive_he.cpp b/test/core/image_processing/adaptive_he.cpp new file mode 100644 index 0000000000..df7068243f --- /dev/null +++ b/test/core/image_processing/adaptive_he.cpp @@ -0,0 +1,113 @@ +// +// Copyright 2020 Debabrata Mandal +// +// 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 + +#include + +namespace gil = boost::gil; + +double epsilon = 1.0; + +std::uint8_t image_matrix[] = +{ + 1, 1, 1, 1, + 3, 3, 3, 3, + 5, 5, 5, 5, + 7, 7, 7, 7 +}; +gil::gray8c_view_t gray_view = gil::interleaved_view(4, 4, reinterpret_cast(image_matrix), 4); + +void check_actual_clip_limit() +{ + gil::histogram h; + for(std::size_t i = 0; i < 100; i++) + { + if (i % 40 == 0) + { + h(i) = 60; + } + else + { + h(i) = 5; + } + } + double limit = 0.01; + double value = gil::detail::actual_clip_limit(h, limit); + + long actual_limit = round(value * h.sum()), max_bin_val = 0; + double excess = 0; + for(std::size_t i = 0; i < 100; i++) + { + if (h(i) > actual_limit) + excess += actual_limit - h(i); + max_bin_val = std::max(max_bin_val, h(i)); + } + BOOST_TEST((abs(excess / h.size() + actual_limit) - limit * h.sum()) < epsilon); +} + +void check_clip_and_redistribute() +{ + gil::histogram h, h2; + for(std::size_t i = 0; i < 100; i++) + { + if (i % 50 == 0) + { + h(i) = 60; + } + else + { + h(i) = 5; + } + } + bool check = true; + double limit = 0.001; + gil::detail::clip_and_redistribute(h, h2, limit); + for(std::size_t i = 0; i < 100; i++) + { + check = check & (abs(limit * h.sum() - h2(i)) < epsilon); + } + BOOST_TEST(check); +} + +void check_non_overlapping_interpolated_clahe() +{ + { + gil::gray8_image_t img1(4, 4), img2(4, 4), img3(4, 4); + gil::histogram_equalization(gray_view, view(img2)); + gil::non_overlapping_interpolated_clahe(gray_view, view(img3), 8, 8, 1.0); + BOOST_TEST(gil::equal_pixels(view(img2), view(img3))); + } + { + gil::gray8_image_t img1(8, 8), img2(8, 8), img3(4, 4); + gil::copy_pixels(gray_view, gil::subimage_view(view(img1), 0, 0, 4, 4)); + gil::copy_pixels(gray_view, gil::subimage_view(view(img1), 0, 4, 4, 4)); + gil::copy_pixels(gray_view, gil::subimage_view(view(img1), 4, 0, 4, 4)); + gil::copy_pixels(gray_view, gil::subimage_view(view(img1), 4, 4, 4, 4)); + gil::histogram_equalization(gray_view, view(img3)); + gil::non_overlapping_interpolated_clahe(view(img1), view(img2), 8, 8, 1.0); + BOOST_TEST(gil::equal_pixels(gil::subimage_view(view(img2), 0, 0, 4, 4), view(img3))); + BOOST_TEST(gil::equal_pixels(gil::subimage_view(view(img2), 0, 4, 4, 4), view(img3))); + BOOST_TEST(gil::equal_pixels(gil::subimage_view(view(img2), 4, 0, 4, 4), view(img3))); + BOOST_TEST(gil::equal_pixels(gil::subimage_view(view(img2), 4, 4, 4, 4), view(img3))); + } +} + +int main() +{ + check_actual_clip_limit(); + check_clip_and_redistribute(); + check_non_overlapping_interpolated_clahe(); + + return boost::report_errors(); +} From 81b4dc08bd9177bf9361194a66b0e70cfd6438e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 25 Jan 2021 02:34:04 +0100 Subject: [PATCH 020/193] ci: Add configuration for GitHub Actions (#544) Add basic GitHub Actions configuration based on mp11 Remove Actions jobs using GCC 4.7 and 4.8 - unsupported compilers Run b2 with --abbreviate-paths on Windows The -std=c++1z is broken for clang-4.0 but no need to test it Add -mbig-obj to GCC on Windows - That is to avoid string table overflow and file too big Define _GLIBCXX_USE_CXX11_ABI=0 for clang 3.5, 3.6, 3.7 - Should help avoid linker error: `undefined reference to std::ios_base::failure::failure(char const*, std::error_code const&)` Disable certain check in algorithm_channel_relation test for clang<3.8 --- .github/workflows/ci.yml | 195 ++++++++++++++++++ .../channel/algorithm_channel_relation.cpp | 6 + 2 files changed, 201 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..880ca948ed --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,195 @@ +name: CI + +on: + pull_request: + push: + branches: + - master + - develop + - feature/** + +env: + LIBRARY: gil + UBSAN_OPTIONS: print_stacktrace=1 + +jobs: + posix: + strategy: + fail-fast: false + matrix: + include: + - toolset: gcc-4.9 + cxxstd: "11" + os: ubuntu-16.04 + install: g++-4.9 + - toolset: gcc-5 + cxxstd: "11,14,1z" + os: ubuntu-16.04 + - toolset: gcc-6 + cxxstd: "11,14,1z" + os: ubuntu-16.04 + install: g++-6 + - toolset: gcc-7 + cxxstd: "11,14,17" + os: ubuntu-18.04 + - toolset: gcc-8 + cxxstd: "11,14,17,2a" + os: ubuntu-18.04 + - toolset: gcc-9 + cxxstd: "11,14,17,2a" + os: ubuntu-18.04 + - toolset: gcc-10 + cxxstd: "11,14,17,2a" + os: ubuntu-18.04 + - toolset: clang + compiler: clang++-3.5 + cxxstd: "11,14" + define: "_GLIBCXX_USE_CXX11_ABI=0" + os: ubuntu-16.04 + install: clang-3.5 + - toolset: clang + compiler: clang++-3.6 + cxxstd: "11,14" + define: "_GLIBCXX_USE_CXX11_ABI=0" + os: ubuntu-16.04 + install: clang-3.6 + - toolset: clang + compiler: clang++-3.7 + cxxstd: "11,14" + define: "_GLIBCXX_USE_CXX11_ABI=0" + os: ubuntu-16.04 + install: clang-3.7 + - toolset: clang + compiler: clang++-3.8 + cxxstd: "11,14" + os: ubuntu-16.04 + install: clang-3.8 + - toolset: clang + compiler: clang++-3.9 + cxxstd: "11,14" + os: ubuntu-16.04 + install: clang-3.9 + - toolset: clang + compiler: clang++-4.0 + cxxstd: "11,14" + os: ubuntu-16.04 + install: clang-4.0 + - toolset: clang + compiler: clang++-5.0 + cxxstd: "11,14,1z" + os: ubuntu-16.04 + install: clang-5.0 + - toolset: clang + compiler: clang++-6.0 + cxxstd: "11,14,17" + os: ubuntu-18.04 + - toolset: clang + compiler: clang++-7 + cxxstd: "11,14,17" + os: ubuntu-18.04 + install: clang-7 + - toolset: clang + compiler: clang++-8 + cxxstd: "11,14,17,2a" + os: ubuntu-20.04 + - toolset: clang + compiler: clang++-9 + cxxstd: "11,14,17,2a" + os: ubuntu-20.04 + - toolset: clang + compiler: clang++-10 + cxxstd: "11,14,17,2a" + os: ubuntu-20.04 + - toolset: clang + cxxstd: "11,14,17,2a" + os: macos-10.15 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Install packages + if: matrix.install + run: sudo apt install ${{matrix.install}} + + - name: Setup Boost + run: | + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + ./bootstrap.sh + ./b2 -d0 headers + + - name: Create user-config.jam + if: matrix.compiler + run: | + echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam + + - name: Run tests + if: "!matrix.define" + run: | + cd ../boost-root + ./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} variant=debug,release + + - name: Run tests + if: matrix.define + run: | + cd ../boost-root + ./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} define=${{matrix.define}} variant=debug,release + + windows: + strategy: + fail-fast: false + matrix: + include: + - toolset: msvc-14.1 + cxxstd: "14,17,latest" + addrmd: 32,64 + os: windows-2016 + - toolset: msvc-14.2 + cxxstd: "14,17,latest" + addrmd: 32,64 + os: windows-2019 + - toolset: gcc + cxxstd: "11,14,17,2a" + addrmd: 64 + os: windows-2019 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Setup Boost + shell: cmd + run: | + if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% + set BOOST_BRANCH=develop + if "%GITHUB_BASE_REF%" == "master" set BOOST_BRANCH=master + cd .. + git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% + cmd /c bootstrap + b2 -d0 headers + + - name: Run tests + if: startsWith(matrix.toolset, 'msvc') + shell: cmd + run: | + cd ../boost-root + b2 -j3 --abbreviate-paths libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release + - name: Run tests + if: startsWith(matrix.toolset, 'gcc') + shell: cmd + run: | + cd ../boost-root + b2 -j3 --abbreviate-paths libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} cxxflags=-mbig-obj variant=debug,release diff --git a/test/core/channel/algorithm_channel_relation.cpp b/test/core/channel/algorithm_channel_relation.cpp index bad3a1d4bc..e7c301b7fe 100644 --- a/test/core/channel/algorithm_channel_relation.cpp +++ b/test/core/channel/algorithm_channel_relation.cpp @@ -31,7 +31,13 @@ void test_channel_relation() BOOST_TEST_GT(f.max_v_, f.min_v_); BOOST_TEST_NE(f.max_v_, f.min_v_); BOOST_TEST_EQ(f.min_v_, f.min_v_); +#if !defined(BOOST_CLANG) || (__clang_major__ == 3 && __clang_minor__ >= 8) + // This particular test fails with optimised build using clang 3.5 or 3.6 + // for unknown reasons. Volunteers are welcome to debug and confirm it is + // either the compiler bug or the library bug: + // b2 toolset=clang variant=release cxxstd=11 define=_GLIBCXX_USE_CXX11_ABI=0 libs/gil/test/core/channel//algorithm_channel_relation BOOST_TEST_NE(f.min_v_, one); // comparable to integral +#endif } struct test_channel_value From a37f12b3e9e90c968e2e416d33c339fa26e77226 Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Tue, 26 Jan 2021 04:31:39 +0600 Subject: [PATCH 021/193] Add implementation of Hough transforms (#512) Support construction from step_size, step_count, and a function for angles Implement angle and radious version of Hough line transform and adds a demo with static line that goes over secondary diagonal. Implement incremental line raster Implement naive line raster Implement Bresenham line raster Leave only Bresenham line rasterization Naive and incremental algorithms were removed because they are supposed to produce the same results anyway. The reason for diverging results is inaccuracy of floating point numbers Add circle rendering through trigonometric functions, using arctan(1 / (radius + 1)) as minimal angle step. Trigonometric circle rasterizer does not follow circle equation, but still produces very round shapes. A new testing methodology needs to be devised for this rasterizer. The new version accepts start and points inclusively and tries to use canonic representation during computations. Slope decided to be is (diff_y + 1) / (diff_x + 1). --- example/hough_transform_circle.cpp | 55 +++++++ example/hough_transform_line.cpp | 71 +++++++++ example/rasterizer_circle.cpp | 33 ++++ example/rasterizer_line.cpp | 42 +++++ include/boost/gil.hpp | 8 +- .../gil/image_processing/hough_parameter.hpp | 112 ++++++++++++++ .../gil/image_processing/hough_transform.hpp | 138 +++++++++++++++++ include/boost/gil/rasterization/circle.hpp | 126 +++++++++++++++ include/boost/gil/rasterization/line.hpp | 97 ++++++++++++ test/core/CMakeLists.txt | 1 + test/core/Jamfile | 1 + test/core/image_processing/CMakeLists.txt | 5 +- test/core/image_processing/Jamfile | 2 + .../hough_circle_transform.cpp | 83 ++++++++++ .../image_processing/hough_line_transform.cpp | 99 ++++++++++++ .../core/image_processing/hough_parameter.cpp | 78 ++++++++++ test/core/rasterization/CMakeLists.txt | 27 ++++ test/core/rasterization/Jamfile | 12 ++ test/core/rasterization/circle.cpp | 76 +++++++++ test/core/rasterization/line.cpp | 144 ++++++++++++++++++ 20 files changed, 1207 insertions(+), 3 deletions(-) create mode 100644 example/hough_transform_circle.cpp create mode 100644 example/hough_transform_line.cpp create mode 100644 example/rasterizer_circle.cpp create mode 100644 example/rasterizer_line.cpp create mode 100644 include/boost/gil/image_processing/hough_parameter.hpp create mode 100644 include/boost/gil/image_processing/hough_transform.hpp create mode 100644 include/boost/gil/rasterization/circle.hpp create mode 100644 include/boost/gil/rasterization/line.hpp create mode 100644 test/core/image_processing/hough_circle_transform.cpp create mode 100644 test/core/image_processing/hough_line_transform.cpp create mode 100644 test/core/image_processing/hough_parameter.cpp create mode 100644 test/core/rasterization/CMakeLists.txt create mode 100644 test/core/rasterization/Jamfile create mode 100644 test/core/rasterization/circle.cpp create mode 100644 test/core/rasterization/line.cpp diff --git a/example/hough_transform_circle.cpp b/example/hough_transform_circle.cpp new file mode 100644 index 0000000000..9f83da9d0d --- /dev/null +++ b/example/hough_transform_circle.cpp @@ -0,0 +1,55 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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; + +int main() +{ + const std::size_t size = 128; + gil::gray8_image_t input_image(size, size); + auto input = gil::view(input_image); + + const std::ptrdiff_t circle_radius = 16; + const gil::point_t circle_center = {64, 64}; + const auto rasterizer = gil::midpoint_circle_rasterizer{}; + std::vector circle_points(rasterizer.point_count(circle_radius)); + rasterizer(circle_radius, circle_center, circle_points.begin()); + for (const auto& point : circle_points) + { + input(point) = std::numeric_limits::max(); + } + + const auto radius_parameter = + gil::hough_parameter::from_step_count(circle_radius, 3, 3); + const auto x_parameter = + gil::hough_parameter::from_step_count(circle_center.x, 3, 3); + const auto y_parameter = + gil::hough_parameter::from_step_count(circle_center.x, 3, 3); + + std::vector parameter_space_images( + radius_parameter.step_count, + gil::gray16_image_t(x_parameter.step_count, y_parameter.step_count)); + std::vector parameter_space_views(parameter_space_images.size()); + std::transform(parameter_space_images.begin(), parameter_space_images.end(), + parameter_space_views.begin(), + [](gil::gray16_image_t& img) + { + return gil::view(img); + }); + + gil::hough_circle_transform_brute(input, radius_parameter, x_parameter, y_parameter, + parameter_space_views.begin(), rasterizer); + std::cout << parameter_space_views[3](3, 3) << '\n'; +} diff --git a/example/hough_transform_line.cpp b/example/hough_transform_line.cpp new file mode 100644 index 0000000000..29c8fc6def --- /dev/null +++ b/example/hough_transform_line.cpp @@ -0,0 +1,71 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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; + +int main() +{ + std::ptrdiff_t size = 32; + gil::gray16_image_t input_image(size, size); + auto input_view = gil::view(input_image); + + // fill secondary diagonal with ones + // do note that origin is located at upper left, + // not bottom left as in usual plots + for (std::ptrdiff_t i = 0; i < size; ++i) + { + input_view(i, size - i - 1) = 1; + } + + // print vertically flipped for better understanding of origin location + for (std::ptrdiff_t y = size - 1; y >= 0; --y) + { + for (std::ptrdiff_t x = 0; x < size; ++x) + { + std::cout << input_view(x, y)[0] << ' '; + } + std::cout << '\n'; + } + + double minimum_theta_step = std::atan(1.0 / size); + // this is the expected theta + double _45_degrees = gil::detail::pi / 4; + double _5_degrees = gil::detail::pi / 36; + std::size_t step_count = 5; + auto theta_parameter = + gil::make_theta_parameter(_45_degrees, _5_degrees, input_view.dimensions()); + auto expected_radius = static_cast(std::round(std::cos(_45_degrees) * size)); + auto radius_parameter = + gil::hough_parameter::from_step_size(expected_radius, 7, 1); + gil::gray32_image_t accumulator_array_image(theta_parameter.step_count, + radius_parameter.step_count); + auto accumulator_array = gil::view(accumulator_array_image); + gil::hough_line_transform(input_view, accumulator_array, theta_parameter, radius_parameter); + std::cout << "expecting maximum at theta=" << _45_degrees << " and radius=" << expected_radius + << '\n'; + for (std::size_t theta_index = 0; theta_index < theta_parameter.step_count; ++theta_index) + { + for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; + ++radius_index) + { + double current_theta = + theta_parameter.start_point + theta_index * theta_parameter.step_size; + std::ptrdiff_t current_radius = + radius_parameter.start_point + radius_parameter.step_size * radius_index; + std::cout << "theta: " << current_theta << " radius: " << current_radius + << " accumulated value: " << accumulator_array(theta_index, radius_index)[0] + << '\n'; + } + } +} diff --git a/example/rasterizer_circle.cpp b/example/rasterizer_circle.cpp new file mode 100644 index 0000000000..4d2997afb0 --- /dev/null +++ b/example/rasterizer_circle.cpp @@ -0,0 +1,33 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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; + +int main() +{ + const std::ptrdiff_t size = 256; + gil::gray8_image_t buffer_image(size, size); + auto buffer = gil::view(buffer_image); + + const std::ptrdiff_t radius = 64; + const auto rasterizer = gil::trigonometric_circle_rasterizer{}; + std::vector circle_points(rasterizer.point_count(radius)); + rasterizer(radius, {128, 128}, circle_points.begin()); + for (const auto& point : circle_points) + { + buffer(point) = std::numeric_limits::max(); + } + + gil::write_view("circle.png", buffer, gil::png_tag{}); +} diff --git a/example/rasterizer_line.cpp b/example/rasterizer_line.cpp new file mode 100644 index 0000000000..9f02a347a4 --- /dev/null +++ b/example/rasterizer_line.cpp @@ -0,0 +1,42 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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; + +const std::ptrdiff_t size = 256; + +void line_bresenham(std::ptrdiff_t width, std::ptrdiff_t height, const std::string& output_name) +{ + const auto rasterizer = gil::bresenham_line_rasterizer{}; + std::vector line_points(rasterizer.point_count(width, height)); + + gil::gray8_image_t image(size, size); + auto view = gil::view(image); + + rasterizer({0, 0}, {width - 1, height - 1}, line_points.begin()); + for (const auto& point : line_points) + { + view(point) = std::numeric_limits::max(); + } + + gil::write_view(output_name, view, gil::png_tag{}); +} + +int main() +{ + line_bresenham(256, 256, "line-bresenham-256-256.png"); + line_bresenham(256, 128, "line-bresenham-256-128.png"); + line_bresenham(256, 1, "line-bresenham-256-1.png"); + line_bresenham(1, 256, "line-bresenham-1-256.png"); +} diff --git a/include/boost/gil.hpp b/include/boost/gil.hpp index b5db505d9b..797b1e6006 100644 --- a/include/boost/gil.hpp +++ b/include/boost/gil.hpp @@ -24,6 +24,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -38,13 +42,13 @@ #include #include #include +#include +#include #include #include #include #include #include #include -#include -#include #endif diff --git a/include/boost/gil/image_processing/hough_parameter.hpp b/include/boost/gil/image_processing/hough_parameter.hpp new file mode 100644 index 0000000000..97fcd8cde5 --- /dev/null +++ b/include/boost/gil/image_processing/hough_parameter.hpp @@ -0,0 +1,112 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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_HOUGH_PARAMETER_HPP +#define BOOST_GIL_IMAGE_PROCESSING_HOUGH_PARAMETER_HPP + +#include +#include +#include + +namespace boost +{ +namespace gil +{ +/// \ingroup HoughTransform +/// \brief A type to encapsulate Hough transform parameter range +/// +/// This type provides a way to express value range for a parameter +/// as well as some factory functions to simplify initialization +template +struct hough_parameter +{ + T start_point; + T step_size; + std::size_t step_count; + + /// \ingroup HoughTransform + /// \brief Create Hough parameter from value neighborhood and step count + /// + /// This function will take start_point as middle point, and in both + /// directions will try to walk half_step_count times until distance of + /// neighborhood is reached + static hough_parameter from_step_count(T start_point, T neighborhood, + std::size_t half_step_count) + { + T step_size = neighborhood / half_step_count; + std::size_t step_count = half_step_count * 2 + 1; + // explicitly fill out members, as aggregate init will error out with narrowing + hough_parameter parameter; + parameter.start_point = start_point - neighborhood; + parameter.step_size = step_size; + parameter.step_count = step_count; + return parameter; + } + + /// \ingroup HoughTransform + /// \brief Create Hough parameter from value neighborhood and step size + /// + /// This function will take start_point as middle point, and in both + /// directions will try to walk step_size at a time until distance of + /// neighborhood is reached + static hough_parameter from_step_size(T start_point, T neighborhood, T step_size) + { + std::size_t step_count = + 2 * static_cast(std::floor(neighborhood / step_size)) + 1; + // do not use step_size - neighborhood, as step_size might not allow + // landing exactly on that value when starting from start_point + // also use parentheses on step_count / 2 because flooring is exactly + // what we want + + // explicitly fill out members, as aggregate init will error out with narrowing + hough_parameter parameter; + parameter.start_point = start_point - step_size * (step_count / 2); + parameter.step_size = step_size; + parameter.step_count = step_count; + return parameter; + } +}; + +/// \ingroup HoughTransform +/// \brief Calculate minimum angle which would be observable if walked on a circle +/// +/// When drawing a circle or moving around a point in circular motion, it is +/// important to not do too many steps, but also to not have disconnected +/// trajectory. This function will calculate the minimum angle that is observable +/// when walking on a circle or tilting a line. +/// WARNING: do keep in mind IEEE 754 quirks, e.g. no-associativity, +/// no-commutativity and precision. Do not expect expressions that are +/// mathematically the same to produce the same values +inline double minimum_angle_step(point_t dimensions) +{ + auto longer_dimension = dimensions.x > dimensions.y ? dimensions.x : dimensions.y; + return std::atan2(1, longer_dimension); +} + +/// \ingroup HoughTransform +/// \brief Create a Hough transform parameter with optimal angle step +/// +/// Due to computational intensity and noise sensitivity of Hough transform, +/// having any candidates missed or computed again is problematic. This function +/// will properly encapsulate optimal value range around approx_angle with amplitude of +/// neighborhood in each direction. +/// WARNING: do keep in mind IEEE 754 quirks, e.g. no-associativity, +/// no-commutativity and precision. Do not expect expressions that are +/// mathematically the same to produce the same values +inline hough_parameter make_theta_parameter(double approx_angle, double neighborhood, + point_t dimensions) +{ + auto angle_step = minimum_angle_step(dimensions); + + // std::size_t step_count = + // 2 * static_cast(std::floor(neighborhood / angle_step)) + 1; + // return {approx_angle - angle_step * (step_count / 2), angle_step, step_count}; + return hough_parameter::from_step_size(approx_angle, neighborhood, angle_step); +} +}} // namespace boost::gil +#endif diff --git a/include/boost/gil/image_processing/hough_transform.hpp b/include/boost/gil/image_processing/hough_transform.hpp new file mode 100644 index 0000000000..6fd7148886 --- /dev/null +++ b/include/boost/gil/image_processing/hough_transform.hpp @@ -0,0 +1,138 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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_HOUGH_TRANSFORM_HPP +#define BOOST_GIL_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace gil { +/// \defgroup HoughTransform +/// \brief A family of shape detectors that are specified by equation +/// +/// Hough transform is a method of mapping (voting) an object which can be described by +/// equation to single point in accumulator array (also called parameter space). +/// Each set pixel in edge map votes for every shape it can be part of. +/// Circle and ellipse transforms are very costly to brute force, while +/// non-brute-forcing algorithms tend to gamble on probabilities. + +/// \ingroup HoughTransform +/// \brief Vote for best fit of a line in parameter space +/// +/// The input must be an edge map with grayscale pixels. Be aware of overflow inside +/// accumulator array. The theta parameter is best computed through factory function +/// provided in hough_parameter.hpp +template +void hough_line_transform(const InputView& input_view, const OutputView& accumulator_array, + const hough_parameter& theta, + const hough_parameter& radius) +{ + std::ptrdiff_t r_lower_bound = radius.start_point; + std::ptrdiff_t r_upper_bound = r_lower_bound + radius.step_size * (radius.step_count - 1); + + for (std::ptrdiff_t y = 0; y < input_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < input_view.width(); ++x) + { + if (!input_view(x, y)[0]) + { + continue; + } + + for (std::size_t theta_index = 0; theta_index < theta.step_count; ++theta_index) + { + double theta_current = + theta.start_point + theta.step_size * static_cast(theta_index); + std::ptrdiff_t current_r = + std::llround(static_cast(x) * std::cos(theta_current) + + static_cast(y) * std::sin(theta_current)); + if (current_r < r_lower_bound || current_r > r_upper_bound) + { + continue; + } + std::size_t r_index = static_cast( + std::llround((current_r - radius.start_point) / radius.step_size)); + // one more safety guard to not get out of bounds + if (r_index < radius.step_count) + { + accumulator_array(theta_index, r_index)[0] += 1; + } + } + } + } +} + +/// \ingroup HoughTransform +/// \brief Vote for best fit of a circle in parameter space according to rasterizer +/// +/// The input must be an edge map with grayscale pixels. Be aware of overflow inside +/// accumulator array. Rasterizer is used to rasterize a circle for voting. The circle +/// then is translated for every origin (x, y) in x y parameter space. For available +/// circle rasterizers, please look at rasterization/circle.hpp +template +void hough_circle_transform_brute(const ImageView& input, + const hough_parameter radius_parameter, + const hough_parameter x_parameter, + const hough_parameter& y_parameter, + ForwardIterator d_first, Rasterizer rasterizer) +{ + const auto width = input.width(); + const auto height = input.height(); + for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; ++radius_index) + { + const auto radius = radius_parameter.start_point + + radius_parameter.step_size * static_cast(radius_index); + std::vector circle_points(rasterizer.point_count(radius)); + rasterizer(radius, {0, 0}, circle_points.begin()); + // sort by scanline to improve cache coherence for row major images + std::sort(circle_points.begin(), circle_points.end(), + [](const point_t& lhs, const point_t& rhs) { return lhs.y < rhs.y; }); + const auto translate = [](std::vector& points, point_t offset) { + std::transform(points.begin(), points.end(), points.begin(), [offset](point_t point) { + return point_t(point.x + offset.x, point.y + offset.y); + }); + }; + + // in case somebody passes iterator to likes of std::vector + typename std::iterator_traits::reference current_image = *d_first; + + // the algorithm has to traverse over parameter space and look at input, instead + // of vice versa, as otherwise it will call translate too many times, as input + // is usually bigger than the coordinate portion of parameter space. + // This might cause extensive cache misses + for (std::size_t x_index = 0; x_index < x_parameter.step_count; ++x_index) + { + for (std::size_t y_index = 0; y_index < y_parameter.step_count; ++y_index) + { + const std::ptrdiff_t x = x_parameter.start_point + x_index * x_parameter.step_size; + const std::ptrdiff_t y = y_parameter.start_point + y_index * y_parameter.step_size; + + auto translated_circle = circle_points; + translate(translated_circle, {x, y}); + for (const auto& point : translated_circle) + { + if (input(point)) + { + ++current_image(x_index, y_index)[0]; + } + } + } + } + ++d_first; + } +} + +}} // namespace boost::gil + +#endif diff --git a/include/boost/gil/rasterization/circle.hpp b/include/boost/gil/rasterization/circle.hpp new file mode 100644 index 0000000000..31c3cf6caf --- /dev/null +++ b/include/boost/gil/rasterization/circle.hpp @@ -0,0 +1,126 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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_RASTERIZATION_CIRCLE_HPP +#define BOOST_GIL_RASTERIZATION_CIRCLE_HPP + +#include +#include +#include +#include + +namespace boost { namespace gil { +/// \defgroup CircleRasterization +/// \ingroup Rasterization +/// \brief Circle rasterization algorithms +/// +/// The main problems are connectivity and equation following. Circle can be easily moved +/// to new offset, and rotation has no effect on it (not recommended to do rotation). + +/// \ingroup CircleRasterization +/// \brief Rasterize trigonometric circle according to radius by sine and radius by cosine +/// +/// This rasterizer is the one used that is used in standard Hough circle transform in +/// the books. It is also quite expensive to compute. +/// WARNING: the product of this rasterizer does not follow circle equation, even though it +/// produces quite round like shapes. +struct trigonometric_circle_rasterizer +{ + /// \brief Calculates minimum angle step that is distinguishable when walking on circle + /// + /// It is important to not have disconnected circle and to not compute unnecessarily, + /// thus the result of this function is used when rendering. + double minimum_angle_step(std::ptrdiff_t radius) const noexcept + { + const auto diameter = radius * 2 - 1; + return std::atan2(1.0, diameter); + } + + /// \brief Calculate the amount of points that rasterizer will output + std::ptrdiff_t point_count(std::ptrdiff_t radius) const noexcept + { + return 8 * static_cast( + std::round(detail::pi / 4 / minimum_angle_step(radius)) + 1); + } + + /// \brief perform rasterization and output into d_first + template + void operator()(std::ptrdiff_t radius, point_t offset, RandomAccessIterator d_first) const + { + const double minimum_angle_step = std::atan2(1.0, radius); + auto translate_mirror_points = [&d_first, offset](point_t p) { + *d_first++ = point_t{offset.x + p.x, offset.y + p.y}; + *d_first++ = point_t{offset.x + p.x, offset.y - p.y}; + *d_first++ = point_t{offset.x - p.x, offset.y + p.y}; + *d_first++ = point_t{offset.x - p.x, offset.y - p.y}; + *d_first++ = point_t{offset.x + p.y, offset.y + p.x}; + *d_first++ = point_t{offset.x + p.y, offset.y - p.x}; + *d_first++ = point_t{offset.x - p.y, offset.y + p.x}; + *d_first++ = point_t{offset.x - p.y, offset.y - p.x}; + }; + const std::ptrdiff_t iteration_count = point_count(radius) / 8; + double angle = 0; + // do note that + 1 was done inside count estimation, thus <= is not needed, only < + for (std::ptrdiff_t i = 0; i < iteration_count; ++i, angle += minimum_angle_step) + { + std::ptrdiff_t x = static_cast(std::round(radius * std::cos(angle))); + std::ptrdiff_t y = static_cast(std::round(radius * std::sin(angle))); + translate_mirror_points({x, y}); + } + } +}; + +/// \ingroup CircleRasterization +/// \brief Perform circle rasterization according to Midpoint algorithm +/// +/// This algorithm givess reasonable output and is cheap to compute. +/// reference: +/// https://en.wikipedia.org/wiki/Midpoint_circle_algorithm +struct midpoint_circle_rasterizer +{ + /// \brief Calculate the amount of points that rasterizer will output + std::ptrdiff_t point_count(std::ptrdiff_t radius) const noexcept + { + // the reason for pulling 8 out is so that when the expression radius * cos(45 degrees) + // is used, it would yield the same result as here + // + 1 at the end is because the point at radius itself is computed as well + return 8 * static_cast( + std::round(radius * std::cos(boost::gil::detail::pi / 4)) + 1); + } + + /// \brief perform rasterization and output into d_first + template + void operator()(std::ptrdiff_t radius, point_t offset, RAIterator d_first) const + { + auto translate_mirror_points = [&d_first, offset](point_t p) { + *d_first++ = point_t{offset.x + p.x, offset.y + p.y}; + *d_first++ = point_t{offset.x + p.x, offset.y - p.y}; + *d_first++ = point_t{offset.x - p.x, offset.y + p.y}; + *d_first++ = point_t{offset.x - p.x, offset.y - p.y}; + *d_first++ = point_t{offset.x + p.y, offset.y + p.x}; + *d_first++ = point_t{offset.x + p.y, offset.y - p.x}; + *d_first++ = point_t{offset.x - p.y, offset.y + p.x}; + *d_first++ = point_t{offset.x - p.y, offset.y - p.x}; + }; + std::ptrdiff_t iteration_distance = point_count(radius) / 8; + std::ptrdiff_t y_current = radius; + std::ptrdiff_t r_squared = radius * radius; + translate_mirror_points({0, y_current}); + for (std::ptrdiff_t x = 1; x < iteration_distance; ++x) + { + std::ptrdiff_t midpoint = x * x + y_current * y_current - y_current - r_squared; + if (midpoint > 0) + { + --y_current; + } + translate_mirror_points({x, y_current}); + } + } +}; +}} // namespace boost::gil +#endif diff --git a/include/boost/gil/rasterization/line.hpp b/include/boost/gil/rasterization/line.hpp new file mode 100644 index 0000000000..1ff91b6a35 --- /dev/null +++ b/include/boost/gil/rasterization/line.hpp @@ -0,0 +1,97 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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 + +namespace boost +{ +namespace gil +{ +/// \defgroup Rasterization +/// \brief A set of functions to rasterize shapes +/// +/// Due to images being discrete, most shapes require specialized algorithms to +/// handle rasterization efficiently and solve problem of connectivity and being +/// close to the original shape. + +/// \defgroup LineRasterization +/// \ingroup Rasterization +/// \brief A set of rasterizers for lines +/// +/// The main problem with line rasterization is to do it efficiently, e.g. less +/// floating point operations. There are multiple algorithms that on paper +/// should reach the same result, but due to quirks of IEEE-754 they don't. +/// Please select one and stick to it if possible. At the moment only Bresenham +/// rasterizer is implemented. + +/// \ingroup LineRasterization +/// \brief Rasterize a line according to Bresenham algorithm +/// +/// Do note that if either width or height is 1, slope is set to zero. +/// reference: +/// https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#:~:text=Bresenham's%20line%20algorithm%20is%20a,straight%20line%20between%20two%20points. +struct bresenham_line_rasterizer +{ + constexpr std::ptrdiff_t point_count(std::ptrdiff_t width, std::ptrdiff_t height) const noexcept + { + return width > height ? width : height; + } + + std::ptrdiff_t point_count(point_t start, point_t end) const noexcept + { + const auto abs_width = std::abs(end.x - start.x) + 1; + const auto abs_height = std::abs(end.y - start.y) + 1; + return point_count(abs_width, abs_height); + } + + template + void operator()(point_t start, point_t end, RandomAccessIterator d_first) const + { + if (start == end) + { + // put the point and immediately exit, as later on division by zero will + // occur + *d_first = start; + return; + } + + auto width = std::abs(end.x - start.x) + 1; + auto height = std::abs(end.y - start.y) + 1; + bool const needs_flip = width < height; + if (needs_flip) + { + // transpose the coordinate system if uncomfortable angle detected + std::swap(width, height); + std::swap(start.x, start.y); + std::swap(end.x, end.y); + } + std::ptrdiff_t const x_increment = end.x >= start.x ? 1 : -1; + std::ptrdiff_t const y_increment = end.y >= start.y ? 1 : -1; + double const slope = + height == 1 ? 0 : static_cast(height) / static_cast(width); + std::ptrdiff_t y = start.y; + double error_term = 0; + for (std::ptrdiff_t x = start.x; x != end.x; x += x_increment) + { + // transpose coordinate system back to proper form if needed + *d_first++ = needs_flip ? point_t{y, x} : point_t{x, y}; + error_term += slope; + if (error_term >= 0.5) + { + --error_term; + y += y_increment; + } + } + *d_first++ = needs_flip ? point_t{end.y, end.x} : end; + } +}; + +}} // namespace boost::gil diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 6abbfdee88..2c812d014b 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -40,3 +40,4 @@ add_subdirectory(image_view) add_subdirectory(algorithm) add_subdirectory(image_processing) add_subdirectory(histogram) +add_subdirectory(rasterization) diff --git a/test/core/Jamfile b/test/core/Jamfile index 45992e30ee..c7c03ee376 100644 --- a/test/core/Jamfile +++ b/test/core/Jamfile @@ -33,3 +33,4 @@ build-project image_view ; build-project algorithm ; build-project image_processing ; build-project histogram ; +build-project rasterization ; diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index 2ad32f4f28..2fc51c1d4a 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -35,7 +35,10 @@ foreach(_name box_filter median_filter sobel_scharr - anisotropic_diffusion) + anisotropic_diffusion + hough_parameter + hough_line_transform + hough_circle_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 be4c755c02..a67c4d9f21 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -20,3 +20,5 @@ run sobel_scharr.cpp ; run box_filter.cpp ; run median_filter.cpp ; run anisotropic_diffusion.cpp ; +run hough_line_transform.cpp ; +run hough_circle_transform.cpp ; diff --git a/test/core/image_processing/hough_circle_transform.cpp b/test/core/image_processing/hough_circle_transform.cpp new file mode 100644 index 0000000000..9b0a3563da --- /dev/null +++ b/test/core/image_processing/hough_circle_transform.cpp @@ -0,0 +1,83 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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 + +#include +#include +#include +#include + +namespace gil = boost::gil; + +template +void exact_fit_test(std::ptrdiff_t radius, gil::point_t offset, Rasterizer rasterizer) +{ + std::vector circle_points(rasterizer.point_count(radius)); + rasterizer(radius, offset, circle_points.begin()); + // const std::ptrdiff_t diameter = radius * 2 - 1; + const std::ptrdiff_t width = offset.x + radius + 1; + const std::ptrdiff_t height = offset.y + radius + 1; + gil::gray8_image_t image(width, height); + auto input = gil::view(image); + + for (const auto& point : circle_points) + { + input(point) = std::numeric_limits::max(); + } + + using param_t = gil::hough_parameter; + const auto radius_parameter = param_t{radius, 0, 1}; + // const auto x_parameter = param_t::from_step_count(offset.x, neighborhood, half_step_count); + // const auto y_parameter = param_t::from_step_count(offset.y, neighborhood, half_step_count); + const auto x_parameter = param_t{offset.x, 0, 1}; + const auto y_parameter = param_t{offset.y, 0, 1}; + + std::vector output_images( + radius_parameter.step_count, + gil::gray16_image_t(x_parameter.step_count, y_parameter.step_count)); + std::vector output_views(radius_parameter.step_count); + std::transform(output_images.begin(), output_images.end(), output_views.begin(), + [](gil::gray16_image_t& img) + { + return gil::view(img); + }); + gil::hough_circle_transform_brute(input, radius_parameter, x_parameter, y_parameter, + output_views.begin(), rasterizer); + if (output_views[0](0, 0) != rasterizer.point_count(radius)) + { + std::cout << "accumulated value: " << static_cast(output_views[0](0, 0)) + << " expected value: " << rasterizer.point_count(radius) << "\n\n"; + } + BOOST_TEST(output_views[0](0, 0) == rasterizer.point_count(radius)); +} + +int main() +{ + const int test_dim_length = 20; + for (std::ptrdiff_t radius = 5; radius < test_dim_length; ++radius) + { + for (std::ptrdiff_t x_offset = radius; x_offset < radius + test_dim_length; ++x_offset) + { + for (std::ptrdiff_t y_offset = radius; y_offset < radius + test_dim_length; ++y_offset) + { + + exact_fit_test(radius, {x_offset, y_offset}, gil::midpoint_circle_rasterizer{}); + exact_fit_test(radius, {x_offset, y_offset}, + gil::trigonometric_circle_rasterizer{}); + } + } + } + + return boost::report_errors(); +} diff --git a/test/core/image_processing/hough_line_transform.cpp b/test/core/image_processing/hough_line_transform.cpp new file mode 100644 index 0000000000..f0ed992fa3 --- /dev/null +++ b/test/core/image_processing/hough_line_transform.cpp @@ -0,0 +1,99 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gil = boost::gil; + +const std::ptrdiff_t width = 64; + +void translate(std::vector& points, std::ptrdiff_t intercept) +{ + std::transform(points.begin(), points.end(), points.begin(), + [intercept](gil::point_t point) + { + return gil::point_t{point.x, point.y + intercept}; + }); +} + +void hough_line_test(std::ptrdiff_t height, std::ptrdiff_t intercept) +{ + const auto rasterizer = gil::bresenham_line_rasterizer{}; + gil::gray8_image_t image(width, width, gil::gray8_pixel_t(0)); + auto input = gil::view(image); + std::vector line_points(rasterizer.point_count(width, height)); + rasterizer({0, 0}, {width - 1, height - 1}, line_points.begin()); + translate(line_points, intercept); + for (const auto& p : line_points) + { + input(p) = 255; + } + + double alpha = std::atan2(height, width); + const double theta = alpha + gil::detail::pi / 2; + const auto minimum_angle_step = gil::minimum_angle_step({width, height}); + const double expected_alpha = std::round(alpha / minimum_angle_step) * minimum_angle_step; + const double expected_radius = std::round(intercept * std::cos(expected_alpha)); + const double expected_theta = std::round(theta / minimum_angle_step) * minimum_angle_step; + + const std::size_t half_step_count = 3; + const std::ptrdiff_t expected_index = 3; + const std::size_t accumulator_array_dimensions = half_step_count * 3 + 1; + auto radius_param = + gil::hough_parameter::from_step_count(expected_radius, 3, half_step_count); + auto theta_param = gil::make_theta_parameter( + expected_theta, minimum_angle_step * half_step_count, {width, height}); + gil::gray32_image_t accumulator_array_image( + accumulator_array_dimensions, accumulator_array_dimensions, gil::gray32_pixel_t(0)); + auto accumulator_array = gil::view(accumulator_array_image); + gil::hough_line_transform(input, accumulator_array, theta_param, radius_param); + + auto max_element_iterator = + std::max_element(accumulator_array.begin(), accumulator_array.end()); + gil::point_t candidates[] = { + {expected_index - 1, expected_index - 1}, {expected_index, expected_index - 1}, + {expected_index + 1, expected_index - 1}, {expected_index - 1, expected_index}, + {expected_index, expected_index}, {expected_index + 1, expected_index}, + {expected_index - 1, expected_index + 1}, {expected_index, expected_index + 1}, + {expected_index + 1, expected_index + 1}}; + bool match_found = false; + for (std::size_t i = 0; i < 9; ++i) + { + if (*max_element_iterator == accumulator_array(candidates[i])) + { + match_found = true; + break; + } + } + BOOST_TEST(match_found); +} + +int main() +{ + for (std::ptrdiff_t height = 1; height < width; ++height) + { + for (std::ptrdiff_t intercept = 1; intercept < width - height; ++intercept) + { + hough_line_test(height, intercept); + } + } + return boost::report_errors(); +} diff --git a/test/core/image_processing/hough_parameter.cpp b/test/core/image_processing/hough_parameter.cpp new file mode 100644 index 0000000000..8d1c7b4452 --- /dev/null +++ b/test/core/image_processing/hough_parameter.cpp @@ -0,0 +1,78 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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 + +namespace gil = boost::gil; + +void from_step_count_test() +{ + const double middle_point = 0.5; + const std::size_t step_count = 5; + const double neighborhood = 1.0; + auto param = + gil::hough_parameter::from_step_count(middle_point, neighborhood, step_count); + BOOST_TEST(param.start_point == middle_point - neighborhood); + BOOST_TEST(param.step_count == step_count * 2 + 1); + BOOST_TEST(param.step_size == neighborhood / step_count); + + bool middle_point_occured = false; + for (std::size_t i = 0; i < param.step_count; ++i) + { + auto current = param.start_point + param.step_size * i; + if (current == middle_point) + { + middle_point_occured = true; + break; + } + } + BOOST_TEST(middle_point_occured); +} + +void from_step_size_test(const double middle_point, const double step_size, + const double neighborhood) +{ + const std::size_t expected_step_count = + static_cast(neighborhood / step_size) * 2 + 1; + auto param = + gil::hough_parameter::from_step_size(middle_point, neighborhood, step_size); + BOOST_TEST(param.start_point == middle_point - step_size * std::floor(expected_step_count / 2)); + BOOST_TEST(param.step_count == expected_step_count); + BOOST_TEST(param.step_size == step_size); + + bool middle_point_occured = false; + for (std::size_t i = 0; i < param.step_count; ++i) + { + auto current = param.start_point + param.step_size * i; + if (current == middle_point) + { + middle_point_occured = true; + break; + } + } + BOOST_TEST(middle_point_occured); +} + +void minimum_step_angle_test(const std::ptrdiff_t width, const std::ptrdiff_t height) +{ + const auto bigger_dim = width > height ? width : height; + const double expected_angle = std::atan2(1.0, bigger_dim); + BOOST_TEST(expected_angle == gil::minimum_angle_step({width, height})); +} + +int main() +{ + from_step_count_test(); + // ideal case + from_step_size_test(2.0, 0.25, 1.0); + from_step_size_test(5.0, 2, 5.0); + minimum_step_angle_test(1200, 800); + minimum_step_angle_test(800, 1200); + return boost::report_errors(); +} diff --git a/test/core/rasterization/CMakeLists.txt b/test/core/rasterization/CMakeLists.txt new file mode 100644 index 0000000000..2ab6dd2b43 --- /dev/null +++ b/test/core/rasterization/CMakeLists.txt @@ -0,0 +1,27 @@ +# Boost.GIL (Generic Image Library) - tests +# +# Copyright 2020 Olzhas Zhumabek +# +# 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) +# +foreach(_name + line + circle) + set(_test t_core_rasterization_${_name}) + set(_target test_core_rasterization_${_name}) + + add_executable(${_target} "") + target_sources(${_target} PRIVATE ${_name}.cpp) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK) + add_test(NAME ${_test} COMMAND ${_target}) + + unset(_name) + unset(_target) +endforeach() diff --git a/test/core/rasterization/Jamfile b/test/core/rasterization/Jamfile new file mode 100644 index 0000000000..750eb46697 --- /dev/null +++ b/test/core/rasterization/Jamfile @@ -0,0 +1,12 @@ +# Boost.GIL (Generic Image Library) - tests +# +# Copyright 2020 Olzhas Zhumabek +# +# 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) +# +import testing ; + +run line.cpp ; +run circle.cpp ; diff --git a/test/core/rasterization/circle.cpp b/test/core/rasterization/circle.cpp new file mode 100644 index 0000000000..68a91f4fd3 --- /dev/null +++ b/test/core/rasterization/circle.cpp @@ -0,0 +1,76 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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 test_rasterizer_follows_equation(std::ptrdiff_t radius, Rasterizer rasterizer) +{ + + std::vector circle_points(rasterizer.point_count(radius)); + std::ptrdiff_t r_squared = radius * radius; + rasterizer(radius, {0, 0}, circle_points.begin()); + std::vector first_octant(rasterizer.point_count(radius) / 8); + + for (std::size_t i = 0, octant_index = 0; i < circle_points.size(); i += 8, ++octant_index) + { + first_octant[octant_index] = circle_points[i]; + } + + for (const auto& point : first_octant) + { + double y_exact = std::sqrt(radius * radius - point.x * point.x); + std::ptrdiff_t lower_result = static_cast(std::floor(y_exact)); + std::ptrdiff_t upper_result = static_cast(std::ceil(y_exact)); + BOOST_TEST(point.y >= lower_result && point.y <= upper_result); + } +} + +template +void test_connectivity(std::ptrdiff_t radius, Rasterizer rasterizer) +{ + std::vector circle_points(rasterizer.point_count(radius)); + rasterizer(radius, {radius, radius}, circle_points.begin()); + for (std::size_t i = 0; i < 8; ++i) + { + std::vector octant(circle_points.size() / 8); + for (std::size_t octant_index = i, index = 0; octant_index < circle_points.size(); + octant_index += 8, ++index) + { + octant[index] = circle_points[octant_index]; + } + + for (std::size_t index = 1; index < octant.size(); ++index) + { + const auto diff_x = std::abs(octant[index].x - octant[index - 1].x); + const auto diff_y = std::abs(octant[index].y - octant[index - 1].y); + BOOST_TEST_LE(diff_x, 1); + BOOST_TEST_LE(diff_y, 1); + } + } +} + +int main() +{ + for (std::ptrdiff_t radius = 5; radius <= 512; ++radius) + { + test_rasterizer_follows_equation(radius, gil::midpoint_circle_rasterizer{}); + // TODO: find out a new testing procedure for trigonometric rasterizer + // test_equation_following(radius, gil::trigonometric_circle_rasterizer{}); + test_connectivity(radius, gil::midpoint_circle_rasterizer{}); + test_connectivity(radius, gil::trigonometric_circle_rasterizer{}); + } + + return boost::report_errors(); +} diff --git a/test/core/rasterization/line.cpp b/test/core/rasterization/line.cpp new file mode 100644 index 0000000000..42e3bf7469 --- /dev/null +++ b/test/core/rasterization/line.cpp @@ -0,0 +1,144 @@ +// Boost.GIL (Generic Image Library) - tests +// +// Copyright 2020 Olzhas Zhumabek +// +// 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 +#include +#include +#include +#include + +namespace gil = boost::gil; + +namespace boost +{ +namespace gil +{ +std::ostream& operator<<(std::ostream& os, const point_t p) +{ + os << "{x=" << p.x << ", y=" << p.y << "}"; + return os; +} +}} // namespace boost::gil + +using line_type = std::vector; + +struct endpoints +{ + gil::point_t start; + gil::point_t end; +}; + +endpoints create_endpoints(std::mt19937& twister, + std::uniform_int_distribution& distr) +{ + gil::point_t start{distr(twister), distr(twister)}; + gil::point_t end{distr(twister), distr(twister)}; + return {start, end}; +} + +line_type create_line(endpoints points) +{ + gil::bresenham_line_rasterizer rasterizer; + line_type forward_line(rasterizer.point_count(points.start, points.end)); + rasterizer(points.start, points.end, forward_line.begin()); + return forward_line; +} + +void test_start_end(const line_type& line_points, endpoints points) +{ + BOOST_TEST_EQ(line_points.front(), points.start); + BOOST_TEST_EQ(line_points.back(), points.end); +} + +// Look at TODO below +// void test_two_way_equivalence(const line_type& forward, line_type backward) +// { +// std::reverse(backward.begin(), backward.end()); +// BOOST_TEST_ALL_EQ(forward.begin(), forward.end(), backward.begin(), backward.end()); +// } + +void test_connectivity(line_type const& line_points) +{ + for (std::size_t i = 1; i < line_points.size(); ++i) + { + const auto x_diff = std::abs(line_points[i].x - line_points[i - 1].x); + const auto y_diff = std::abs(line_points[i].y - line_points[i - 1].y); + BOOST_TEST_LE(x_diff, 1); + BOOST_TEST_LE(y_diff, 1); + } +} + +void test_bresenham_rasterizer_follows_equation(line_type line_points) +{ + auto start = line_points.front(); + auto end = line_points.back(); + + auto width = std::abs(end.x - start.x) + 1; + auto height = std::abs(end.y - start.y) + 1; + if (width < height) + { + std::swap(width, height); + std::transform(line_points.begin(), line_points.end(), line_points.begin(), + [](gil::point_t p) + { + return gil::point_t{p.y, p.x}; + }); + // update start and end + start = line_points.front(); + end = line_points.back(); + } + const double sign = [start, end]() + { + auto const width_sign = end.x < start.x; + auto const height_sign = end.y < start.y; + auto const slope_sign = width_sign != height_sign; + return slope_sign ? -1 : 1; + }(); + const double slope = static_cast(height) / static_cast(width); + const double intercept = + static_cast(start.y) - sign * slope * static_cast(start.x); + for (const auto& point : line_points) + { + double const expected_y = sign * slope * static_cast(point.x) + intercept; + auto const difference = + std::abs(point.y - static_cast(std::round(expected_y))); + BOOST_TEST_LE(difference, static_cast(slope + 1)); + } +} + +int main() +{ + const std::ptrdiff_t size = 256; + for (std::size_t seed = 0; seed <= 100; ++seed) + { + std::mt19937 twister(seed); + std::uniform_int_distribution distr(0, size - 1); + const std::size_t sample_count = 100; + for (std::size_t sample_index = 0; sample_index < sample_count; ++sample_index) + { + auto endpoints = create_endpoints(twister, distr); + auto forward_line = create_line(endpoints); + test_start_end(forward_line, endpoints); + // TODO: figure out if forward/backward equivalence is possible to provide + // auto backward_line = create_line({endpoints.end, endpoints.start}); + // test_two_way_equivalence(forward_line, backward_line); + test_connectivity(forward_line); + // test_connectivity(backward_line); + test_bresenham_rasterizer_follows_equation(forward_line); + // test_bresenham_rasterizer_follows_equation(backward_line); + } + } + + return boost::report_errors(); +} From 434e78f76b5179369738f25a553b74cbaf886218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 26 Jan 2021 01:05:03 +0100 Subject: [PATCH 022/193] ci: R.I.P. Travis CI - Long live GitHub Actions Follows: - https://github.com/boostorg/gil/pull/544 Outstanding tasks: - https://github.com/boostorg/gil/issues/549 - https://github.com/boostorg/gil/issues/548 --- .ci/upload_docs.sh | 53 --------- .travis.yml | 283 --------------------------------------------- CMakeLists.txt | 2 +- CONTRIBUTING.md | 10 +- README.md | 8 +- test/Jamfile | 2 +- 6 files changed, 11 insertions(+), 347 deletions(-) delete mode 100755 .ci/upload_docs.sh delete mode 100644 .travis.yml diff --git a/.ci/upload_docs.sh b/.ci/upload_docs.sh deleted file mode 100755 index f8b98b5f55..0000000000 --- a/.ci/upload_docs.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -set -e # Exit with nonzero exit code if anything fails - -SOURCE_BRANCH="master" -TARGET_BRANCH="gh-pages" - -# Pull requests and commits to other branches shouldn't try to deploy, just build to verify -if [ "$TRAVIS_PULL_REQUEST" != "false" ] || \ - [ "$TRAVIS_BRANCH" != master -a \ - "$TRAVIS_BRANCH" != develop -a \ - "$TRAVIS_BRANCH" != doc -a \ - "$TRAVIS_BRANCH" != ci ]; then - echo "No docs to upload." - exit 0 -fi - -if [ -z "$GH_TOKEN" ]; then - echo "Error: GH_TOKEN is undefined" - exit 1 -fi - -# Save some useful information -REPO=`git config remote.origin.url` -SHA=`git rev-parse --verify HEAD` - -# doc happens to contain the "html" tree that we want to push -# into the gh-pages branch. So we step into that directory, create a new repo, -# set the remote appropriately, then commit and push. -cd doc -git init -git config user.name "Travis CI" -git config user.email "travis-ci" - -# Make sure 'GH_TOKEN' is set (as 'secure' variable) in .travis.yml -git remote add upstream "https://$GH_TOKEN@github.com/boostorg/gil.git" -git fetch upstream -git reset upstream/gh-pages - -# Prepare version. -if [ "$TRAVIS_BRANCH" = develop -o "$TRAVIS_BRANCH" = doc ]; then - mkdir -p develop/doc/ - cp ../index.html develop/ - cp ../doc/index.html develop/doc/ - cp -a html develop/doc/ - git add -A develop -else - git add html index.html -fi -# Commit the new version. -git commit -m "Deploy to GitHub Pages: ${SHA}" - -# Now that we're all set up, we can push. -git push -q upstream HEAD:gh-pages diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5c18d49687..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,283 +0,0 @@ -# Copyright 2016 Peter Dimov -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) -dist: xenial - -language: cpp - -sudo: false - -os: - - linux - - osx - -env: - matrix: - - BOGUS_JOB=true - -matrix: - fast_finish: true - exclude: - - env: BOGUS_JOB=true - include: - - os: linux - env: COMPILER=g++-6 VARIANT=debug TOOLSET=gcc CXXSTD=11 B2_OPTIONS="coverage=on" - addons: - apt: - packages: - - g++-6 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - - lcov - sources: - - ubuntu-toolchain-r-test - after_success: - - bash libs/gil/.ci/coverage.sh - - - os: linux - env: COMPILER=g++-5 VARIANT=debug TOOLSET=gcc CXXSTD=11 TEST_HEADERS=1 - addons: - apt: - packages: - - g++-5 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=g++-5 VARIANT=release TOOLSET=gcc CXXSTD=14 - addons: - apt: - packages: - - g++-5 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=g++-6 VARIANT=debug TOOLSET=gcc CXXSTD=14 - addons: - apt: - packages: - - g++-6 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=g++-6 VARIANT=release TOOLSET=gcc CXXSTD=11 - addons: - apt: - packages: - - g++-6 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=g++-7 VARIANT=debug TOOLSET=gcc CXXSTD=17 - addons: - apt: - packages: - - g++-7 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=g++-7 VARIANT=release TOOLSET=gcc CXXSTD=17 - addons: - apt: - packages: - - g++-7 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=g++-9 VARIANT=debug TOOLSET=gcc CXXSTD=2a - addons: - apt: - packages: - - g++-9 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=g++-9 VARIANT=release TOOLSET=gcc CXXSTD=2a - addons: - apt: - packages: - - g++-9 - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=clang++-3.9 VARIANT=debug TOOLSET=clang CXXSTD=11 - addons: - apt: - packages: - - clang-3.9 - - libstdc++-6-dev - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.9 - - - os: linux - env: COMPILER=clang++-3.9 VARIANT=release TOOLSET=clang CXXSTD=11 - addons: - apt: - packages: - - clang-3.9 - - libstdc++-6-dev - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.9 - - - os: linux - env: COMPILER=clang++-6.0 VARIANT=gil_ubsan_integer TOOLSET=clang CXXSTD=11 B2_OPTIONS="visibility=global" UBSAN_OPTIONS='print_stacktrace=1' - addons: - apt: - packages: - - clang-6.0 - - libstdc++-7-dev - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=clang++-6.0 VARIANT=gil_ubsan_nullability TOOLSET=clang CXXSTD=11 B2_OPTIONS="visibility=global" UBSAN_OPTIONS='print_stacktrace=1' - addons: - apt: - packages: - - clang-6.0 - - libstdc++-7-dev - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: linux - env: COMPILER=clang++-6.0 VARIANT=gil_ubsan_undefined TOOLSET=clang CXXSTD=11 B2_OPTIONS="visibility=global" UBSAN_OPTIONS='print_stacktrace=1' - addons: - apt: - packages: - - clang-6.0 - - libstdc++-7-dev - - libpng-dev - - libjpeg-dev - - libtiff5-dev - - libraw-dev - sources: - - ubuntu-toolchain-r-test - - - os: osx - env: COMPILER=clang++ VARIANT=debug TOOLSET=clang CXXSTD=11 - - - os: osx - env: COMPILER=clang++ VARIANT=release TOOLSET=clang CXXSTD=11 - - - env: - - DOC=1 - - secure: "UHit2f6Hq2pkHvx8rfrQvFacYiQKVO3vrCbNuDi/VSAIzQjRnqCqE06y4vpXLMsXf62TvBeCBStIuI8g+HP8B+f39oGb/9Om+yIgN/yes47R4sLFKFbRiOS6sfCIefJp7Kx7GSFf81xWxStpIU4QaSsk8Dlt5xyurTWXFSde+lQ=" - addons: - apt: - packages: - - doxygen - - python3 - - python3-pip - - python3-setuptools - -install: - - |- - if [ "$DOC" ]; then - pip3 --version - pip3 install --upgrade pip - pip3 install --user sphinx>=2.1 - sphinx-build --version - fi - - cd .. - - $TRAVIS_BUILD_DIR/.ci/get-boost.sh $TRAVIS_BRANCH $TRAVIS_BUILD_DIR - - cd boost-root - - export BOOST_ROOT=$(pwd) - - ./bootstrap.sh - -script: - - |- - if [ "$DOC" ]; then - echo "using doxygen ;" > ~/user-config.jam - ./b2 libs/gil/doc - else - echo "using $TOOLSET : : $COMPILER : ;" > ~/user-config.jam - ./b2 -j 2 $B2_OPTIONS toolset=$TOOLSET variant=$VARIANT cxxstd=$CXXSTD libs/gil/test/core - ./b2 -j 2 $B2_OPTIONS toolset=$TOOLSET variant=$VARIANT cxxstd=$CXXSTD libs/gil/test/legacy - ./b2 -j 2 $B2_OPTIONS toolset=$TOOLSET variant=$VARIANT cxxstd=$CXXSTD libs/gil/test/extension/dynamic_image - ./b2 -j 2 $B2_OPTIONS toolset=$TOOLSET variant=$VARIANT cxxstd=$CXXSTD libs/gil/test/extension/numeric - ./b2 -j 2 $B2_OPTIONS toolset=$TOOLSET variant=$VARIANT cxxstd=$CXXSTD libs/gil/test/extension/toolbox - ./b2 -j 2 $B2_OPTIONS toolset=$TOOLSET variant=$VARIANT cxxstd=$CXXSTD libs/gil/test/extension/io//simple - fi - -after_success: -# Upload docs only when building upstream. - - |- - if [ "$DOC" -a \ - "$TRAVIS_REPO_SLUG" = "boostorg/gil" -a \ - "$TRAVIS_PULL_REQUEST" = "false" ]; then - export GH_TOKEN - cd libs/gil - .ci/upload_docs.sh - fi - -notifications: - email: - on_success: always - webhooks: - urls: - - https://webhooks.gitter.im/e/f59b626f2ed08f3d30ab - # options: [always|never|change] - on_success: change # default: always - on_failure: always # default: always - on_start: change # default: never - on_cancel: always # default: always - on_error: always # default: always diff --git a/CMakeLists.txt b/CMakeLists.txt index 52327b933a..fc5e60123b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ message(STATUS "Boost.GIL: Require C++${CMAKE_CXX_STANDARD}") set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -# Avoid warnings flood on Travis CI, AppVeyor, CircleCI, Azure Pipelines +# Avoid warnings flood from CI builds if(DEFINED ENV{CI} OR DEFINED ENV{AGENT_JOBSTATUS} OR DEFINED ENV{GITHUB_ACTIONS}) set(BOOST_GIL_BUILD_CI ON) message(STATUS "Boost.GIL: Turning off detailed compiler warnings for CI build short log") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aae35c138f..058cb45ebd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -233,10 +233,10 @@ git push feature/foo Finally, sign in to your GitHub account and [create a pull request](https://help.github.com/articles/creating-a-pull-request/). -Your pull request will be automatically built and tests will run on Travis CI -and AppVeyor (see [README](README.md) for builds status). Please, keep an eye -on those CI builds and correct any problems detected in your contribution -by updating your pull request. +Your pull request will be automatically built and tests will run on GitHub ACtions, +Azure Pipelines, AppVeyor and Circle CI (see [README](README.md) for builds status). +Please, keep an eye on those CI builds and correct any problems detected in your +contribution by updating your pull request. ### 5. Update your pull request @@ -300,7 +300,7 @@ request from reviewer, just add new commits: cd libs/gil git checkout feature/foo git add -A -git commit -m "Fix build Travis CI failures" +git commit -m "Fix CI build failure" git push feature/foo ``` diff --git a/README.md b/README.md index 0c7367c59a..62895dae58 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://bintray.com/bincrafters/public-conan/boost_gil%3Abincrafters) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://github.com/Microsoft/vcpkg/tree/master/ports/boost-gil) -Documentation | AppVeyor | Azure Pipelines | Travis CI | CircleCI | Regression ---------------|-----------------|-----------------|-----------------|-----------------|------------ -[![develop](https://img.shields.io/badge/doc-develop-blue.svg)](https://boostorg.github.io/gil/develop/) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h/branch/develop?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/develop) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=develop)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=develop) | [![Travis](https://travis-ci.org/boostorg/gil.svg?branch=develop)](https://travis-ci.org/boostorg/gil) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/develop.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/develop) | [![gil](https://img.shields.io/badge/gil-develop-blue.svg)](http://www.boost.org/development/tests/develop/developer/gil.html) -[![master](https://img.shields.io/badge/doc-master-blue.svg)](https://boostorg.github.io/gil/) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/master) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=master)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=master) | [![Travis](https://travis-ci.org/boostorg/gil.svg?branch=master)](https://travis-ci.org/boostorg/gil) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/master.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/master) | [![gil](https://img.shields.io/badge/gil-master-blue.svg)](http://www.boost.org/development/tests/master/developer/gil.html) +Documentation | GitHub Actions | Azure Pipelines | CircleCI | Regression +--------------|----------------|-----------------|-----------------|------------ +[![develop](https://img.shields.io/badge/doc-develop-blue.svg)](https://boostorg.github.io/gil/develop/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/gil/actions?query=branch:develop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h/branch/develop?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/develop) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=develop)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=develop) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/develop.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/develop) | [![gil](https://img.shields.io/badge/gil-develop-blue.svg)](http://www.boost.org/development/tests/develop/developer/gil.html) +[![master](https://img.shields.io/badge/doc-master-blue.svg)](https://boostorg.github.io/gil/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=master)](https://github.com/boostorg/gil/actions?query=branch:master) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/master) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=master)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=master) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/master.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/master) | [![gil](https://img.shields.io/badge/gil-master-blue.svg)](http://www.boost.org/development/tests/master/developer/gil.html) # Boost.GIL diff --git a/test/Jamfile b/test/Jamfile index cfca2f4436..f787b72a86 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -16,7 +16,7 @@ import regex ; import sequence ; import testing ; -# Avoid warnings flood on Travis CI, AppVeyor, CircleCI, Azure Pipelines, GitHub Actions +# Avoid warnings flood from CI builds if ! [ os.environ CI ] && ! [ os.environ AGENT_JOBSTATUS ] && ! [ os.environ GITHUB_ACTIONS ] { DEVELOPMENT_EXTRA_WARNINGS = From 4e0f815f5e27edb315093a3f632f611bb7ffad16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 27 Jan 2021 07:07:51 +0100 Subject: [PATCH 023/193] build: Remove most of uses of cxxflags from test/Jamfile (#550) Most uses of cxxflags are wrong. Use b2 warnings instead, e.g. warnings=pedantic --- test/Jamfile | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/test/Jamfile b/test/Jamfile index f787b72a86..99fed7abd5 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -16,32 +16,12 @@ import regex ; import sequence ; import testing ; -# Avoid warnings flood from CI builds -if ! [ os.environ CI ] && ! [ os.environ AGENT_JOBSTATUS ] && ! [ os.environ GITHUB_ACTIONS ] -{ - DEVELOPMENT_EXTRA_WARNINGS = - msvc:"-W4" - gcc:"-pedantic -Wextra -Wcast-align -Wconversion -Wfloat-equal -Wshadow -Wsign-promo -Wstrict-aliasing -Wunused-parameter" - clang,debug:"-pedantic -Wextra -Wcast-align -Wconversion -Wfloat-equal -Wshadow -Wsign-promo -Wstrict-aliasing -Wunused-parameter -Wsign-conversion" - clang,release:"-pedantic -Wextra -Wcast-align -Wconversion -Wfloat-equal -Wshadow -Wsign-promo -Wstrict-aliasing -Wunused-parameter -Wsign-conversion" - darwin:"-pedantic -Wextra -Wcast-align -Wconversion -Wfloat-equal -Wshadow -Wsign-promo -Wstrict-aliasing -Wunused-parameter" - ; -} -else -{ - DEVELOPMENT_EXTRA_WARNINGS = - msvc:"-W1" - ; -} - project : requirements . # TODO: Enable concepts check for all, not just test/core #BOOST_GIL_USE_CONCEPT_CHECK=1 - msvc:"-bigobj" - msvc:on msvc:_SCL_SECURE_NO_DEPRECATE msvc:_CRT_SECURE_NO_WARNINGS msvc:_CRT_NONSTDC_NO_DEPRECATE From 6f0a061363ffa1f1ed0b2bcd509d3c0ce2a058a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Thu, 28 Jan 2021 09:21:29 +0100 Subject: [PATCH 024/193] docs: Clarify PR update procedure in CONTRIBUTING.md [ci skip] --- CONTRIBUTING.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 058cb45ebd..5492bd7e52 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,12 +61,13 @@ please follow the workflow explained in this document. - **DO** ensure each commit successfully builds. The entire PR must pass all tests in the Continuous Integration (CI) system before it'll be merged. - **DO** ensure any new features or changes to existing behaviours are covered with test cases. -- **DO** address PR feedback in an additional commit(s) rather than amending the existing - commits, and only rebase/squash them when necessary. This makes it easier for reviewers - to track changes. +- **DO** address PR feedback in an additional commit(s) rather than amending the existing commits. + This makes it easier for reviewers to track changes. +- **DO** sync your PR branch with the upstream `develop` branch frequently resolving any conflicts if necessary. + You can either `git merge upstream/develop` or `git rebase upstream/develop` with `git push --force` for the latter. + The merge may make it easier for reviewers to track changes though. - **DO** assume that the [Squash and Merge] will be used to merge your commit unless you request otherwise in the PR. -- **DO** NOT fix merge conflicts using a merge commit. Prefer git rebase. - **DO** NOT submit changes to the original legacy tests, see [test/legacy/README.md](test/legacy/README.md). From 5d63cf6798c3985cd02aa703274b2c9a85ff24f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Thu, 28 Jan 2021 22:15:50 +0100 Subject: [PATCH 025/193] Fix more warnings in examples Completing https://github.com/boostorg/gil/pull/545 --- example/harris.cpp | 6 +++++- example/hessian.cpp | 10 +++++----- example/hvstack.hpp | 3 +-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/example/harris.cpp b/example/harris.cpp index 81233fe38a..fd9bede286 100644 --- a/example/harris.cpp +++ b/example/harris.cpp @@ -71,7 +71,11 @@ void apply_gaussian_blur(gil::gray8_view_t input_view, gil::gray8_view_t output_ constexpr static auto filterHeight = 5ull; constexpr static auto filterWidth = 5ull; constexpr static double filter[filterHeight][filterWidth] = { - 2, 4, 6, 4, 2, 4, 9, 12, 9, 4, 5, 12, 15, 12, 5, 4, 9, 12, 9, 4, 2, 4, 5, 4, 2, + { 2, 4, 6, 4, 2 }, + { 4, 9, 12, 9, 4 }, + { 5, 12, 15, 12, 5}, + { 4, 9, 12, 9, 4 }, + { 2, 4, 5, 4, 2 } }; constexpr double factor = 1.0 / 159; constexpr double bias = 0.0; diff --git a/example/hessian.cpp b/example/hessian.cpp index d12f51ddb0..e27199f5f4 100644 --- a/example/hessian.cpp +++ b/example/hessian.cpp @@ -64,11 +64,11 @@ void apply_gaussian_blur(gil::gray8_view_t input_view, gil::gray8_view_t output_ constexpr static auto filter_width = 5ull; constexpr static double filter[filter_height][filter_width] = { - 2, 4, 6, 4, 2, - 4, 9, 12, 9, 4, - 5, 12, 15, 12, 5, - 4, 9, 12, 9, 4, - 2, 4, 5, 4, 2, + { 2, 4, 6, 4, 2 }, + { 4, 9, 12, 9, 4 }, + { 5, 12, 15, 12, 5 }, + { 4, 9, 12, 9, 4 }, + { 2, 4, 5, 4, 2 } }; constexpr double factor = 1.0 / 159; constexpr double bias = 0.0; diff --git a/example/hvstack.hpp b/example/hvstack.hpp index 1b4daaaf13..8f120a10b6 100644 --- a/example/hvstack.hpp +++ b/example/hvstack.hpp @@ -1,6 +1,7 @@ #include "boost/gil/image_view_factory.hpp" #include #include +#include #include #include #include @@ -15,7 +16,6 @@ void hstack(const std::vector& views, const View& output_view) } auto height = views.front().height(); - auto width = views.front().width(); for (const auto& view : views) { if (view.height() != height) @@ -50,7 +50,6 @@ image hstack(const std::vector& views) throw std::invalid_argument("empty views vector is passed - cannot create stacked image"); } - auto dimensions = views.front().dimensions(); std::ptrdiff_t full_width = std::accumulate(views.begin(), views.end(), 0, [](std::ptrdiff_t old, const View& view) { return old + view.width(); }); From 063385398fe7690e8b6e6611d0d6e1b851e636d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Thu, 28 Jan 2021 22:32:43 +0100 Subject: [PATCH 026/193] Fix warnings about abs called without std qualification --- .../gil/image_processing/adaptive_histogram_equalization.hpp | 2 +- include/boost/gil/image_processing/histogram_matching.hpp | 4 ++-- test/core/histogram/utilities.cpp | 5 +++-- test/core/image_processing/adaptive_he.cpp | 5 +++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp index 9dcbfba928..d56746094a 100644 --- a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp +++ b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp @@ -63,7 +63,7 @@ double actual_clip_limit(SrcHist const& src_hist, double cliplimit = 0.03) if (v.second > middle) excess += v.second - middle; }); - if (abs(excess - (cliplimit - middle) * num_bins) < epsilon) + if (std::abs(excess - (cliplimit - middle) * num_bins) < epsilon) break; else if (excess > (cliplimit - middle) * num_bins) high = middle - 1; diff --git a/include/boost/gil/image_processing/histogram_matching.hpp b/include/boost/gil/image_processing/histogram_matching.hpp index ea49a5c153..7019e278c6 100644 --- a/include/boost/gil/image_processing/histogram_matching.hpp +++ b/include/boost/gil/image_processing/histogram_matching.hpp @@ -97,8 +97,8 @@ std::map histogram_matching( { start--; } - if (abs(cumltv_refhist[ref_keys[start]] - src_val) > - abs(cumltv_refhist(std::min(ref_max, std::get<0>(ref_keys[start + 1]))) - + if (std::abs(cumltv_refhist[ref_keys[start]] - src_val) > + std::abs(cumltv_refhist(std::min(ref_max, std::get<0>(ref_keys[start + 1]))) - src_val)) { inverse_mapping[std::get<0>(src_keys[j])] = diff --git a/test/core/histogram/utilities.cpp b/test/core/histogram/utilities.cpp index 4f26a05b41..72b40e9477 100644 --- a/test/core/histogram/utilities.cpp +++ b/test/core/histogram/utilities.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -51,7 +52,7 @@ void check_normalize() bool check = true; for (std::size_t i = 0; i < 64; i++) { - check = check & (abs(expected[i] - h1(i)) < epsilon); + check = check & (std::abs(expected[i] - h1(i)) < epsilon); } BOOST_TEST(check); @@ -73,7 +74,7 @@ void check_normalize() bool check2 = true; for (std::size_t i = 0; i < 64; i++) { - check2 = check2 & (abs(expected2[i/8][i%8] - h2(i/8,i%8)) < epsilon); + check2 = check2 & (std::abs(expected2[i/8][i%8] - h2(i/8,i%8)) < epsilon); } BOOST_TEST(check2); } diff --git a/test/core/image_processing/adaptive_he.cpp b/test/core/image_processing/adaptive_he.cpp index df7068243f..b625d0b6b5 100644 --- a/test/core/image_processing/adaptive_he.cpp +++ b/test/core/image_processing/adaptive_he.cpp @@ -13,6 +13,7 @@ #include +#include #include namespace gil = boost::gil; @@ -53,7 +54,7 @@ void check_actual_clip_limit() excess += actual_limit - h(i); max_bin_val = std::max(max_bin_val, h(i)); } - BOOST_TEST((abs(excess / h.size() + actual_limit) - limit * h.sum()) < epsilon); + BOOST_TEST((std::abs(excess / h.size() + actual_limit) - limit * h.sum()) < epsilon); } void check_clip_and_redistribute() @@ -75,7 +76,7 @@ void check_clip_and_redistribute() gil::detail::clip_and_redistribute(h, h2, limit); for(std::size_t i = 0; i < 100; i++) { - check = check & (abs(limit * h.sum() - h2(i)) < epsilon); + check = check & (std::abs(limit * h.sum() - h2(i)) < epsilon); } BOOST_TEST(check); } From 6007d74667f9079ebed4475de48906f3eef573ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Thu, 28 Jan 2021 22:43:31 +0100 Subject: [PATCH 027/193] Fix some clang -Wunused-variable warnings --- .../adaptive_histogram_equalization.hpp | 2 -- test/core/channel/algorithm_channel_relation.cpp | 5 +++-- test/legacy/pixel_iterator.cpp | 10 ++++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp index d56746094a..b1c189d948 100644 --- a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp +++ b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp @@ -161,8 +161,6 @@ void non_overlapping_interpolated_clahe( std::size_t const channels = num_channels::value; coord_t const width = src_view.width(); coord_t const height = src_view.height(); - std::size_t pixel_max = std::numeric_limits::max(); - std::size_t pixel_min = std::numeric_limits::min(); // Find control points diff --git a/test/core/channel/algorithm_channel_relation.cpp b/test/core/channel/algorithm_channel_relation.cpp index e7c301b7fe..28d155f94f 100644 --- a/test/core/channel/algorithm_channel_relation.cpp +++ b/test/core/channel/algorithm_channel_relation.cpp @@ -21,8 +21,6 @@ template void test_channel_relation() { using fixture_t = fixture::channel; - using channel_value_t = typename fixture_t::channel_value_t; - channel_value_t const one = 1; fixture_t f; BOOST_TEST_LE(f.min_v_, f.max_v_); @@ -31,11 +29,14 @@ void test_channel_relation() BOOST_TEST_GT(f.max_v_, f.min_v_); BOOST_TEST_NE(f.max_v_, f.min_v_); BOOST_TEST_EQ(f.min_v_, f.min_v_); + #if !defined(BOOST_CLANG) || (__clang_major__ == 3 && __clang_minor__ >= 8) // This particular test fails with optimised build using clang 3.5 or 3.6 // for unknown reasons. Volunteers are welcome to debug and confirm it is // either the compiler bug or the library bug: // b2 toolset=clang variant=release cxxstd=11 define=_GLIBCXX_USE_CXX11_ABI=0 libs/gil/test/core/channel//algorithm_channel_relation + using channel_value_t = typename fixture_t::channel_value_t; + channel_value_t const one = 1; BOOST_TEST_NE(f.min_v_, one); // comparable to integral #endif } diff --git a/test/legacy/pixel_iterator.cpp b/test/legacy/pixel_iterator.cpp index 1b4a6d2a4a..419f7e649d 100644 --- a/test/legacy/pixel_iterator.cpp +++ b/test/legacy/pixel_iterator.cpp @@ -19,6 +19,11 @@ #include #include +#if defined(BOOST_CLANG) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-variable" +#endif + using namespace boost::gil; using namespace std; @@ -353,3 +358,8 @@ int main() return EXIT_FAILURE; } } + +#if defined(BOOST_CLANG) +#pragma clang diagnostic pop +#endif + From 422ca82fe58d5aed90f86a2b043a402a2dc48a53 Mon Sep 17 00:00:00 2001 From: Cypre55 <57396709+Cypre55@users.noreply.github.com> Date: Fri, 29 Jan 2021 03:12:30 +0530 Subject: [PATCH 028/193] Removed two instaces of boost.mpl (#551) Removed "#include " at include/boost/gil/detail/mp11/hpp and test/core/pixel/test_fixture.hpp --- include/boost/gil/detail/mp11.hpp | 1 - test/core/pixel/test_fixture.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/boost/gil/detail/mp11.hpp b/include/boost/gil/detail/mp11.hpp index 2778978faf..8c4eec0305 100644 --- a/include/boost/gil/detail/mp11.hpp +++ b/include/boost/gil/detail/mp11.hpp @@ -10,7 +10,6 @@ #define BOOST_GIL_DETAIL_MP11_HPP #include -#include // required by dynamic_image and boost::variant (?) namespace boost { namespace gil { namespace detail { diff --git a/test/core/pixel/test_fixture.hpp b/test/core/pixel/test_fixture.hpp index c9ef03f32a..d2633356b7 100644 --- a/test/core/pixel/test_fixture.hpp +++ b/test/core/pixel/test_fixture.hpp @@ -21,7 +21,6 @@ #include #include -#include // for compatibility with Boost.Test #include #include From 2e2764225f81fff3eb90e56b55635880d954f3b0 Mon Sep 17 00:00:00 2001 From: theroyn Date: Sat, 30 Jan 2021 22:11:51 +0200 Subject: [PATCH 029/193] Support constructing a planar image from interleaved image (#552) Fixes #478 --- include/boost/gil/algorithm.hpp | 61 +++++++++++++++++++++++++++++---- test/core/image/image.cpp | 27 ++++++++++----- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/include/boost/gil/algorithm.hpp b/include/boost/gil/algorithm.hpp index b67d54a35c..cbd1716868 100644 --- a/include/boost/gil/algorithm.hpp +++ b/include/boost/gil/algorithm.hpp @@ -734,10 +734,33 @@ void default_construct_pixels(View const& view) namespace detail { +enum class copy_planarity_condition +{ + planar_to_planar, + interleaved_to_planar, + mixed_to_interleaved +}; + +using planar_to_planar_type = + std::integral_constant + < + copy_planarity_condition, copy_planarity_condition::planar_to_planar + >; +using interleaved_to_planar_type = + std::integral_constant + < + copy_planarity_condition, copy_planarity_condition::interleaved_to_planar + >; +using mixed_to_interleaved_type = + std::integral_constant + < + copy_planarity_condition, copy_planarity_condition::mixed_to_interleaved + >; + /// std::uninitialized_copy for pairs of planar iterators template BOOST_FORCEINLINE -void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, std::true_type) +void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, It2 last2, planar_to_planar_type) { std::size_t channel=0; try { @@ -761,13 +784,25 @@ void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, std::true_type) } } -/// std::uninitialized_copy for interleaved or mixed iterators +/// std::uninitialized_copy for interleaved or mixed(planar into interleaved) iterators template BOOST_FORCEINLINE -void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, std::false_type) +void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, It2 last2, mixed_to_interleaved_type) { std::uninitialized_copy(first1, last1, first2); } + +/// std::uninitialized_copy for interleaved to planar iterators +template +BOOST_FORCEINLINE +void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, It2 last2, +interleaved_to_planar_type) +{ + default_construct_aux(first2, last2, std::true_type()); + + typename It2::difference_type n = last2 - first2; + copier_n()(first1, n, first2); +} } // namespace detail /// \ingroup ImageViewSTLAlgorithmsUninitializedCopyPixels @@ -777,13 +812,24 @@ void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, std::false_type) template void uninitialized_copy_pixels(View1 const& view1, View2 const& view2) { - using is_planar = std::integral_constant::value && is_planar::value>; + using copy_planarity_condition = detail::copy_planarity_condition; + using copy_planarity_condition_type = + std::integral_constant + < + copy_planarity_condition, + !is_planar::value + ? copy_planarity_condition::mixed_to_interleaved + : (is_planar::value + ? copy_planarity_condition::planar_to_planar + : copy_planarity_condition::interleaved_to_planar) + >; BOOST_ASSERT(view1.dimensions() == view2.dimensions()); if (view1.is_1d_traversable() && view2.is_1d_traversable()) { detail::uninitialized_copy_aux( - view1.begin().x(), view1.end().x(), view2.begin().x(), is_planar()); + view1.begin().x(), view1.end().x(), view2.begin().x(), view2.end().x(), + copy_planarity_condition_type()); } else { @@ -792,12 +838,13 @@ void uninitialized_copy_pixels(View1 const& view1, View2 const& view2) { for (y = 0; y < view1.height(); ++y) detail::uninitialized_copy_aux( - view1.row_begin(y), view1.row_end(y), view2.row_begin(y), is_planar()); + view1.row_begin(y), view1.row_end(y), view2.row_begin(y), view2.row_end(y), + copy_planarity_condition_type()); } catch(...) { for (typename View1::y_coord_t y0 = 0; y0 < y; ++y0) - detail::destruct_aux(view2.row_begin(y0), view2.row_end(y0), is_planar()); + detail::destruct_aux(view2.row_begin(y0), view2.row_end(y0), is_planar()); throw; } } diff --git a/test/core/image/image.cpp b/test/core/image/image.cpp index 52d7bd7034..df61a3df92 100644 --- a/test/core/image/image.cpp +++ b/test/core/image/image.cpp @@ -56,15 +56,15 @@ struct test_constructor_from_other_image auto v2 = gil::const_view(image2); BOOST_TEST_ALL_EQ(v1.begin(), v1.end(), v2.begin(), v2.end()); } - // { - // //constructor planar from interleaved - // image_t image1(dimensions, rnd_pixel); - // gil::image image2(image1); - // BOOST_TEST_EQ(image2.dimensions(), dimensions); - // auto v1 = gil::const_view(image1); - // auto v2 = gil::const_view(image2); - // BOOST_TEST_ALL_EQ(v1.begin(), v1.end(), v2.begin(), v2.end()); - // } + { + // constructor planar from interleaved + image_t image1(dimensions, rnd_pixel); + gil::image image2(image1); + BOOST_TEST_EQ(image2.dimensions(), dimensions); + auto v1 = gil::const_view(image1); + auto v2 = gil::const_view(image2); + BOOST_TEST_ALL_EQ(v1.begin(), v1.end(), v2.begin(), v2.end()); + } } static void run() { @@ -90,6 +90,15 @@ struct test_constructor_from_view auto v2 = gil::const_view(image2); BOOST_TEST_ALL_EQ(v1.begin(), v1.end(), v2.begin(), v2.end()); } + { + //constructor planar from interleaved + image_t image1(dimensions, rnd_pixel); + auto v1 = gil::transposed_view(gil::const_view(image1)); + gil::image image2(gil::transposed_view(v1)); + BOOST_TEST_EQ(image2.dimensions(), dimensions); + auto v2 = gil::const_view(image2); + BOOST_TEST_ALL_EQ(v1.begin(), v1.end(), v2.begin(), v2.end()); + } } static void run() { From 90f7952b054de873b14196466c35a646b83b2575 Mon Sep 17 00:00:00 2001 From: Scramjet911 <36035352+Scramjet911@users.noreply.github.com> Date: Tue, 2 Feb 2021 23:23:16 +0530 Subject: [PATCH 030/193] Fixed most of warnings in examples and some in core library (#545) --- example/hessian.cpp | 4 ++-- example/mandelbrot.cpp | 2 +- include/boost/gil/channel_algorithm.hpp | 2 +- include/boost/gil/color_base.hpp | 2 +- include/boost/gil/color_convert.hpp | 8 ++++---- include/boost/gil/image_processing/harris.hpp | 2 +- include/boost/gil/image_processing/hessian.hpp | 4 ++-- include/boost/gil/image_processing/numeric.hpp | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/example/hessian.cpp b/example/hessian.cpp index e27199f5f4..cf05050bcc 100644 --- a/example/hessian.cpp +++ b/example/hessian.cpp @@ -60,8 +60,8 @@ gil::gray8_image_t to_grayscale(gil::rgb8_view_t original) void apply_gaussian_blur(gil::gray8_view_t input_view, gil::gray8_view_t output_view) { - constexpr static auto filter_height = 5ull; - constexpr static auto filter_width = 5ull; + constexpr static std::ptrdiff_t filter_height = 5ull; + constexpr static std::ptrdiff_t filter_width = 5ull; constexpr static double filter[filter_height][filter_width] = { { 2, 4, 6, 4, 2 }, diff --git a/example/mandelbrot.cpp b/example/mandelbrot.cpp index 0bcfc2ea14..1aa5a90897 100644 --- a/example/mandelbrot.cpp +++ b/example/mandelbrot.cpp @@ -40,7 +40,7 @@ struct mandelbrot_fn t=pow(t,0.2); value_type ret; - for (int k=0; k::value; ++k) + for (std::size_t k=0; k::value; ++k) ret[k]=(typename channel_type

::type)(_in_color[k]*t + _out_color[k]*(1-t)); return ret; } diff --git a/include/boost/gil/channel_algorithm.hpp b/include/boost/gil/channel_algorithm.hpp index 12ecd01b0a..848c9652ad 100644 --- a/include/boost/gil/channel_algorithm.hpp +++ b/include/boost/gil/channel_algorithm.hpp @@ -79,7 +79,7 @@ struct unsigned_integral_max_value> template struct unsigned_integral_num_bits - : std::integral_constant + : std::integral_constant(sizeof(UnsignedIntegralChannel) * 8)> {}; template diff --git a/include/boost/gil/color_base.hpp b/include/boost/gil/color_base.hpp index a05b409de8..b638aacbba 100644 --- a/include/boost/gil/color_base.hpp +++ b/include/boost/gil/color_base.hpp @@ -84,7 +84,7 @@ struct homogeneous_color_base typename U = Element, typename = typename std::enable_if::value>::type > - homogeneous_color_base() : v0_{} {}; + homogeneous_color_base() : v0_{} {} explicit homogeneous_color_base(Element v) : v0_(v) {} diff --git a/include/boost/gil/color_convert.hpp b/include/boost/gil/color_convert.hpp index 080d758ae9..a4b6427023 100644 --- a/include/boost/gil/color_convert.hpp +++ b/include/boost/gil/color_convert.hpp @@ -170,13 +170,13 @@ struct default_color_converter_impl // Apply color correction, strengthening, reducing non-zero components by // s = 1 / (1 - k) for k < 1, where 1 denotes dst_t max, otherwise s = 1 (literal). uint_t const dst_max = channel_traits::max_value(); - uint_t const s_div = dst_max - k; + uint_t const s_div = static_cast(dst_max - k); if (s_div != 0) { double const s = dst_max / static_cast(s_div); - c = (c - k) * s; - m = (m - k) * s; - y = (y - k) * s; + c = static_cast((c - k) * s); + m = static_cast((m - k) * s); + y = static_cast((y - k) * s); } else { diff --git a/include/boost/gil/image_processing/harris.hpp b/include/boost/gil/image_processing/harris.hpp index c5deedda99..18185afd81 100644 --- a/include/boost/gil/image_processing/harris.hpp +++ b/include/boost/gil/image_processing/harris.hpp @@ -44,7 +44,7 @@ void compute_harris_responses( " tensor from the same image"); } - auto const window_length = weights.size(); + std::ptrdiff_t const window_length = weights.size(); auto const width = m11.width(); auto const height = m11.height(); auto const half_length = window_length / 2; diff --git a/include/boost/gil/image_processing/hessian.hpp b/include/boost/gil/image_processing/hessian.hpp index 2c8c3adeb1..f88c153607 100644 --- a/include/boost/gil/image_processing/hessian.hpp +++ b/include/boost/gil/image_processing/hessian.hpp @@ -50,9 +50,9 @@ inline void compute_hessian_responses( auto ddxx_i = channel_t(); auto ddyy_i = channel_t(); auto dxdy_i = channel_t(); - for (typename OutputView::coord_t w_y = 0; w_y < weights.size(); ++w_y) + for (typename OutputView::coord_t w_y = 0; w_y < static_cast(weights.size()); ++w_y) { - for (typename OutputView::coord_t w_x = 0; w_x < weights.size(); ++w_x) + for (typename OutputView::coord_t w_x = 0; w_x < static_cast(weights.size()); ++w_x) { ddxx_i += ddxx(x + w_x - center, y + w_y - center) .at(std::integral_constant{}) * weights.at(w_x, w_y); diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index 2109c555f4..b601b17dd0 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -48,7 +48,7 @@ inline double lanczos(double x, std::ptrdiff_t a) if (0 <= x && x <= 0) return 1; - if (-a < x && x < a) + if (static_cast(-a) < x && x < static_cast(a)) return normalized_sinc(x) / normalized_sinc(x / static_cast(a)); return 0; From d923a8f37e9b2fa05b57f476076be451acb6ec9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 2 Feb 2021 19:58:29 +0100 Subject: [PATCH 031/193] Declare laplacian_function::get_directed_offsets as inline Fixes linker error due to multiple definitions. The issue was missed during review of #500 as some public headers are still missing from boost/gil.hpp. --- include/boost/gil/image_processing/diffusion.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/gil/image_processing/diffusion.hpp b/include/boost/gil/image_processing/diffusion.hpp index 72f55a2ae4..0c1a3dce75 100644 --- a/include/boost/gil/image_processing/diffusion.hpp +++ b/include/boost/gil/image_processing/diffusion.hpp @@ -106,7 +106,7 @@ namespace laplace_function { and so on in clockwise manner. Leave element as zero if it is not to be computed. */ -std::array get_directed_offsets() +inline std::array get_directed_offsets() { return {point_t{-1, -1}, point_t{0, -1}, point_t{+1, -1}, point_t{+1, 0}, point_t{+1, +1}, point_t{0, +1}, point_t{-1, +1}, point_t{-1, 0}}; From c7847f4d640e0a3ab4bea4dad53af48dd9ad57de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 2 Feb 2021 23:50:58 +0100 Subject: [PATCH 032/193] Fix warning: returning reference to local temporary object (#556) Fixes #475 --- .../extension/toolbox/image_types/subchroma_image.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/boost/gil/extension/toolbox/image_types/subchroma_image.hpp b/include/boost/gil/extension/toolbox/image_types/subchroma_image.hpp index f49d69e286..cd7de24b29 100644 --- a/include/boost/gil/extension/toolbox/image_types/subchroma_image.hpp +++ b/include/boost/gil/extension/toolbox/image_types/subchroma_image.hpp @@ -245,8 +245,8 @@ class subchroma_image_view : public image_view : image_view< locator >( v ) {} - const point_t& v_ssfactors() const { return point_t( get_deref_fn().vx_ssfactor(), get_deref_fn().vx_ssfactor() ); } - const point_t& u_ssfactors() const { return point_t( get_deref_fn().ux_ssfactor(), get_deref_fn().ux_ssfactor() ); } + point_t v_ssfactors() const { return point_t( get_deref_fn().vx_ssfactor(), get_deref_fn().vx_ssfactor() ); } + point_t u_ssfactors() const { return point_t( get_deref_fn().ux_ssfactor(), get_deref_fn().ux_ssfactor() ); } const point_t& y_dimension() const { return _y_dimensions; } const point_t& v_dimension() const { return _v_dimensions; } @@ -256,9 +256,9 @@ class subchroma_image_view : public image_view const plane_locator_t& v_plane() const { return get_deref_fn().v_locator(); } const plane_locator_t& u_plane() const { return get_deref_fn().u_locator(); } - const plane_view_t y_plane_view() const { return plane_view_t( _y_dimensions, y_plane() ); } - const plane_view_t v_plane_view() const { return plane_view_t( _v_dimensions, v_plane() ); } - const plane_view_t u_plane_view() const { return plane_view_t( _u_dimensions, u_plane() ); } + plane_view_t y_plane_view() const { return plane_view_t( _y_dimensions, y_plane() ); } + plane_view_t v_plane_view() const { return plane_view_t( _v_dimensions, v_plane() ); } + plane_view_t u_plane_view() const { return plane_view_t( _u_dimensions, u_plane() ); } private: From 04daae7160474e9d1dbd795e2e10f761d9cfec2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 3 Feb 2021 00:26:14 +0100 Subject: [PATCH 033/193] Fix warning: comparison of integer expressions of different signedness --- include/boost/gil/histogram.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/gil/histogram.hpp b/include/boost/gil/histogram.hpp index bdbf9a6d7b..a74b590033 100644 --- a/include/boost/gil/histogram.hpp +++ b/include/boost/gil/histogram.hpp @@ -183,7 +183,7 @@ struct filler<1> template void operator()(Container& hist, Tuple& lower, Tuple& upper, std::size_t bin_width = 1) { - for (auto i = std::get<0>(lower); std::get<0>(upper) - i >= bin_width; i += bin_width) + for (auto i = std::get<0>(lower); static_cast(std::get<0>(upper) - i) >= bin_width; i += bin_width) { hist(i / bin_width) = 0; } From 58804ecb241063947a0ec34aed972a018f2f425a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 3 Feb 2021 00:47:17 +0100 Subject: [PATCH 034/193] Remove and other cruft from Jamfile (#557) The warnings, debug-symbols and other build properties can (and should) be controlled via b2 command line options. By the way, most of uses in Jamfile-s are usually incorrect. Instead, better option is to: use warnings=pedantic to control the warnings instead of cxxflags options directly either in requirements or in default-build, depending on whether you want to override it from the command line or not --- test/Jamfile | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/Jamfile b/test/Jamfile index 99fed7abd5..341ac443dc 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -22,18 +22,6 @@ project . # TODO: Enable concepts check for all, not just test/core #BOOST_GIL_USE_CONCEPT_CHECK=1 - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_NONSTDC_NO_DEPRECATE - msvc:NOMINMAX - intel:off - gcc:"-fstrict-aliasing" - darwin:"-fstrict-aliasing" - # variant filter for clang is necessary to allow ubsan_* - # custom variants declare distinct set of - clang,debug:"-fstrict-aliasing" - clang,release:"-fstrict-aliasing" - $(DEVELOPMENT_EXTRA_WARNINGS) [ requires cxx11_constexpr cxx11_defaulted_functions From f58f3a23a1c009535fb8f88c36d71dee59e7d13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 3 Feb 2021 00:52:57 +0100 Subject: [PATCH 035/193] Disable warning: overlapping comparisons always evaluate to false [-Wtautological-overlap-compare] --- test/legacy/channel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/legacy/channel.cpp b/test/legacy/channel.cpp index b4225d20e9..f53d170e4a 100644 --- a/test/legacy/channel.cpp +++ b/test/legacy/channel.cpp @@ -17,6 +17,9 @@ #if defined(BOOST_CLANG) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" +#if (__clang_major__ >= 10) +#pragma clang diagnostic ignored "-Wtautological-overlap-compare" +#endif #elif BOOST_GCC >= 40700 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" From 8337f47aeb72e81141f3ea63834bdb1672965bb1 Mon Sep 17 00:00:00 2001 From: Siddharth Date: Thu, 4 Feb 2021 22:31:32 +0530 Subject: [PATCH 036/193] Review and update includes in boost/gil.hpp (#559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #555 Co-authored-by: Mateusz Łoskot --- include/boost/gil.hpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/include/boost/gil.hpp b/include/boost/gil.hpp index 797b1e6006..a2e2831114 100644 --- a/include/boost/gil.hpp +++ b/include/boost/gil.hpp @@ -21,13 +21,10 @@ #include #include #include +#include #include #include #include -#include -#include -#include -#include #include #include #include @@ -42,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -50,5 +48,17 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif From c3b3d35aaabee47df448e95cdeedc8b08f272c87 Mon Sep 17 00:00:00 2001 From: meshtag <63031630+meshtag@users.noreply.github.com> Date: Fri, 5 Feb 2021 16:57:11 +0530 Subject: [PATCH 037/193] Fix sphinx installation link in docs readme (#560) --- doc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.md b/doc/README.md index 48ac0b32a1..de7700be63 100644 --- a/doc/README.md +++ b/doc/README.md @@ -5,7 +5,7 @@ A simple guide about writing and building documentation for Boost.GIL. ## Prerequisites - Python 3 -- [Install Sphinx](#install-sphinx) (see `requirements.txt`) +- Install [Sphinx](https://www.sphinx-doc.org/en/master/index.html) (see `requirements.txt`) - Install [Doxygen](http://www.doxygen.org) From 2676d31801c1232c2824c93e617f5c49a638e594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 9 Feb 2021 23:49:02 +0100 Subject: [PATCH 038/193] ci: Disable AppVeyor job using CMake to avoid timeouts --- .appveyor.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 2608d1e6eb..4ad917bd3f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -29,17 +29,6 @@ environment: CXXSTD: 11 TEST_HEADERS: 1 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - - TOOLSET: msvc-14.1 - ARCH: x86_64 - VARIANT: debug - CXXSTD: 17 - TEST_HEADERS: 1 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - - TOOLSET: msvc-14.1 - ARCH: x86_64 - VARIANT: release - CXXSTD: 17 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - TOOLSET: msvc-14.1 ARCH: x86_64 VARIANT: debug @@ -64,10 +53,21 @@ environment: - TOOLSET: msvc-14.1 ARCH: x86_64 VARIANT: debug - CXXSTD: 14 - GENERATOR: "Visual Studio 15 2017 Win64" - CMAKE_CONFIG: Debug + CXXSTD: 17 + TEST_HEADERS: 1 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + - TOOLSET: msvc-14.1 + ARCH: x86_64 + VARIANT: release + CXXSTD: 17 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + # - TOOLSET: msvc-14.1 + # ARCH: x86_64 + # VARIANT: debug + # CXXSTD: 14 + # GENERATOR: "Visual Studio 15 2017 Win64" + # CMAKE_CONFIG: Debug + # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 matrix: fast_finish: true From 1e8526797e3327fd1bed7c2ace092fad5fd5ee74 Mon Sep 17 00:00:00 2001 From: meshtag <63031630+meshtag@users.noreply.github.com> Date: Sat, 13 Feb 2021 21:31:22 +0530 Subject: [PATCH 039/193] Added all standard morphological transformations (#541) * Added all standard morphological transformations * Improved comments and some other things * Applied adviced changes * Applied adviced changes * Should handle grayscale dilation/erosion * Checking * Added test cases and improved code structure * Added command line control * Added command line control * Rectified some things * Rectified some more things * Improved comments * Improved comments * Improved doxygen comments and added more test cases * Improved compatibility for builds and rectifying whitespace use * Minor improvement in comments * Did clang formatting * pushed enum class inside namespace 'detail' and some other things * Should handle multichannel images * Clang formatting attempt * got rid of if/else comparators for target_element * Adds morphology.hpp declaration in boost/gil.hpp * Fix newline * (std::max)(a, b) instead of std::max(a, b) * Improved Formatting --- example/Jamfile | 1 + example/morphology.cpp | 139 ++++++++ example/morphology_original.png | Bin 0 -> 2082 bytes include/boost/gil.hpp | 1 + .../boost/gil/image_processing/morphology.hpp | 300 ++++++++++++++++++ test/core/image_processing/CMakeLists.txt | 3 +- test/core/image_processing/Jamfile | 1 + test/core/image_processing/morphology.cpp | 136 ++++++++ 8 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 example/morphology.cpp create mode 100644 example/morphology_original.png create mode 100644 include/boost/gil/image_processing/morphology.hpp create mode 100644 test/core/image_processing/morphology.cpp diff --git a/example/Jamfile b/example/Jamfile index 48b9e51aef..df17d57c42 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -30,6 +30,7 @@ local sources = histogram.cpp interleaved_ptr.cpp mandelbrot.cpp + morphology.cpp packed_pixel.cpp resize.cpp sobel_scharr.cpp diff --git a/example/morphology.cpp b/example/morphology.cpp new file mode 100644 index 0000000000..e54bbfef6a --- /dev/null +++ b/example/morphology.cpp @@ -0,0 +1,139 @@ +// +// Copyright 2021 Prathamesh Tagore +// +// 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 +#include + +// Default structuring element is SE = [1,1,1] +// |1,1,1| +// [1,1,1] +// SE(1,1)(center pixel) is the one which coincides with the currently +// considered pixel of the image to be convolved. The structuring element can be +// easily changed by the user. +namespace gil = boost::gil; +int main(int argc, char** argv) +{ + std::map operations; + if (argc < 4 || argc > 11) + { + throw std::invalid_argument( + "Wrong format of command line arguments.\n" + "Correct format is " + " " + "" + " \n"); + // User has to enter atleast one operation and they can enter maximum 8 + // operations considering binary conversion to be an + // operation.Output_image_template argument is the common component which + // will be added in names of all output images followed by a hyphen and + // the operation name. + // Example : + // ./example_morphology morphology_original.png out black_hat top_hat + // morphological_gradient dilation erosion opening closing binary + // Order of arguments entered will not matter with the exception of binary + // operation used for binary morphological operations.If binary is entered + // through the command line, it will always be the first operation to be + // applied. + return -1; + } + else + { + for (int i = 3; i < argc; ++i) + operations[argv[i]] = true; + } + gil::gray8_image_t img; + gil::read_image(argv[1], img, gil::png_tag{}); + + // Image can be converted to a binary format with high value as 255 and low + // value as 0 by using the threshold operator . This can be used for binary + // morphological operations . Convenient threshold for binary conversion may + // be chosen by the user. + if (operations["binary"]) + { + threshold_binary(view(img), view(img), 170, 255); + std::string name = argv[2]; + name += "-binary.png"; + gil::write_view(name, view(img), gil::png_tag{}); + } + + std::vector ker_vec(9, 1.0f); // Structuring element + gil::detail::kernel_2d ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1); + gil::gray8_image_t img_out_dilation(img.dimensions()), img_out_erosion(img.dimensions()), + img_out_opening(img.dimensions()); + gil::gray8_image_t img_out_closing(img.dimensions()), img_out_mg(img.dimensions()), + img_out_top_hat(img.dimensions()); + gil::gray8_image_t img_out_black_hat(img.dimensions()); + + // Do not pass empty input image views in functions defined below for + // morphological operations to avoid errors. + if (operations["dilation"]) + { + // dilate(input_image_view,output_image_view,structuring_element,iterations) + dilate(view(img), view(img_out_dilation), ker_mat, 1); + std::string name = argv[2]; + name += "-dilation.png"; + gil::write_view(name, view(img_out_dilation), gil::png_tag{}); + } + + if (operations["erosion"]) + { + // erode(input_image_view,output_image_view,structuring_element,iterations) + erode(view(img), view(img_out_erosion), ker_mat, 1); + std::string name = argv[2]; + name += "-erosion.png"; + gil::write_view(name, view(img_out_erosion), gil::png_tag{}); + } + + if (operations["opening"]) + { + // opening(input_image_view,output_image_view,structuring_element) + opening(view(img), view(img_out_opening), ker_mat); + std::string name = argv[2]; + name += "-opening.png"; + gil::write_view(name, view(img_out_opening), gil::png_tag{}); + } + + if (operations["closing"]) + { + // closing(input_image_view,output_image_view,structuring_element) + closing(view(img), view(img_out_closing), ker_mat); + std::string name = argv[2]; + name += "-closing.png"; + gil::write_view(name, view(img_out_closing), gil::png_tag{}); + } + + if (operations["morphological_gradient"]) + { + // morphological_gradient(input_image_view,output_image_view,structuring_element) + morphological_gradient(view(img), view(img_out_mg), ker_mat); + std::string name = argv[2]; + name += "-morphological_gradient.png"; + gil::write_view(name, view(img_out_mg), gil::png_tag{}); + } + + if (operations["top_hat"]) + { + // top_hat(input_image_view,output_image_view,structuring_element) + top_hat(view(img), view(img_out_top_hat), ker_mat); + std::string name = argv[2]; + name += "-top_hat.png"; + gil::write_view(name, view(img_out_top_hat), gil::png_tag{}); + } + + if (operations["black_hat"]) + { + // black_hat(input_image_view,output_image_view,structuring_element) + black_hat(view(img), view(img_out_black_hat), ker_mat); + std::string name = argv[2]; + name += "-black_hat.png"; + gil::write_view(name, view(img_out_black_hat), gil::png_tag{}); + } +} diff --git a/example/morphology_original.png b/example/morphology_original.png new file mode 100644 index 0000000000000000000000000000000000000000..b63e99ed9da7b769fd4fe45826f1f5118fb24297 GIT binary patch literal 2082 zcmZ8iZB!Fi8U~6e6lgL-Es3HqNe0s5Za%OUTm@l-02eGGm}5C53=p(+p&(Y24~YWK zK(s~zJ5gHBi8NB>s42CDEv14Z@)2P@Srsd~k^)M*T0KZn5ZybR-Tv4gbMJkhdGEdN zecmtn{lpkA521&Xlap7hir5V2d+@7Y0tY9@y4#~pPKzgFi49wdobPnWkNh0%QaSqS zjxOi@^TQ>-X)lhwvGd>?_lTT(es>3(tFzub(NvAy zIzL_N=%N<~u4pLwB>>1_qlr^ZWhdM<)3v6ru8s!PTca3@j-!I7^6GIpP-Cc@kD( zks#cOtGc|>&2*QNl z-@N~ND`WIcdN{QDW0^FG2q~KzompwR+{E4haX$RR>1DIiIl0YWB(C z_K7Nv)~o{Z13a*r3WW%+5Lo54{A?l}lG1*4jQtiQ8u_K%O}9bf&VeyPaBVBRWQ}Qs zRcaUYQyZ?^ztSFH-i`lH+1sWat0O6k$XzgUB>fM9Ywvt#c=&Nxe;Q zW`6;35cEMMAkh`){i+D%Gf-Lt5(%7#o<5a)H5d=XP>OUcwFe3z| z#s^FUXoj)3*|Mn+_*c{U+VTgCs-?@5=ZDgyW1TXt3HUFSpCtNS%?;aAHk~@2XY(~E zb?Rr~%I3JEtJ`P@dCAD=2_RC*sNSX!^z0zGmYLX2K*BgpbTszJ5|9vZQ2JO3I_5mA9KC5A zgw#;RNyoMLQ;NB zuIpv0R2`-J=4=?w6WDnJ^bvQWhrlk(&5n0-5K6yE)k7$R{kyFWDywzyWnvKDGT;jB zA{Uvnq2IQV|9mw4yu10A$t}u@1SflOIQ&177yja<=p+}nuK}^pB1sJhvjm(4G2q$w zsr3EYEf>OXddj@=v%^R~EF+qha?~~oB&pbDZN+efAm9dEFvC>>JXNI7Qp42(?1rdd zVx84*>I=1@ZT9A?m-CCaS~l(MdrGgS&xF5vazET1ruZ=TN?jkdn?AFDekOeXKVV0F u^w54g2gVt>+wmdSAYM%We=~M;&9AKA^Y#TxX!BmK`NwWdB+f=^4*UmVeU2^w literal 0 HcmV?d00001 diff --git a/include/boost/gil.hpp b/include/boost/gil.hpp index a2e2831114..b98af56e6a 100644 --- a/include/boost/gil.hpp +++ b/include/boost/gil.hpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/gil/image_processing/morphology.hpp b/include/boost/gil/image_processing/morphology.hpp new file mode 100644 index 0000000000..1149b2c165 --- /dev/null +++ b/include/boost/gil/image_processing/morphology.hpp @@ -0,0 +1,300 @@ +// +// Copyright 2021 Prathamesh Tagore +// +// 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_MORPHOLOGY_HPP +#define BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP +#include +#include +#include + +namespace boost +{ +namespace gil +{ +namespace detail +{ +enum class morphological_operation +{ + dilation, + erosion, +}; +/// \addtogroup ImageProcessing +/// @{ + +/// \brief Implements morphological operations at pixel level.This function +/// compares neighbouring pixel values according to the kernel and choose +/// minimum/mamximum neighbouring pixel value and assigns it to the pixel under +/// consideration. +/// \param src_view - Source/Input image view. +/// \param dst_view - View which stores the final result of operations performed by this function. +/// \param kernel - Kernel matrix/structuring element containing 0's and 1's +/// which will be used for applying the required morphological operation. +/// \param identifier - Indicates the type of morphological operation to be applied. +/// \tparam SrcView type of source image. +/// \tparam DstView type of output image. +/// \tparam Kernel type of structuring element. +template +void morph_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel, + morphological_operation identifier) +{ + std::ptrdiff_t flip_ker_row, flip_ker_col, row_boundary, col_boundary; + typename channel_type::type target_element; + for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) + { + for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) + { + target_element = src_view(view_col, view_row); + for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row) + { + flip_ker_row = kernel.size() - 1 - kernel_row; // row index of flipped kernel + + for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col) + { + flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel + + // We ensure that we consider only those pixels which are overlapped + // on a non-zero kernel_element as + if (kernel.at(flip_ker_row, flip_ker_col) == 0) + { + continue; + } + // index of input signal, used for checking boundary + row_boundary = view_row + (kernel.center_y() - flip_ker_row); + col_boundary = view_col + (kernel.center_x() - flip_ker_col); + + // ignore input samples which are out of bound + if (row_boundary >= 0 && row_boundary < src_view.height() && + col_boundary >= 0 && col_boundary < src_view.width()) + { + + if (identifier == morphological_operation::dilation) + { + target_element = + (std::max)(src_view(col_boundary, row_boundary)[0], target_element); + } + else if (identifier == morphological_operation::erosion) + { + target_element = + (std::min)(src_view(col_boundary, row_boundary)[0], target_element); + } + } + } + } + dst_view(view_col, view_row) = target_element; + } + } +} + +/// \brief Checks feasibility of the desired operation and passes parameter +/// values to the function morph_impl alongwith individual channel views of the +/// input image. +/// \param src_view - Source/Input image view. +/// \param dst_view - View which stores the final result of operations performed by this function. +/// \param kernel - Kernel matrix/structuring element containing 0's and 1's +/// which will be used for applying the required morphological operation. +/// \param identifier - Indicates the type of morphological operation to be applied. +/// \tparam SrcView type of source image. +/// \tparam DstView type of output image. +/// \tparam Kernel type of structuring element. +template +void morph(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat, + morphological_operation identifier) +{ + BOOST_ASSERT(ker_mat.size() != 0 && src_view.dimensions() == dst_view.dimensions()); + gil_function_requires>(); + gil_function_requires>(); + + gil_function_requires::type, + typename color_space_type::type>>(); + + gil::image intermediate_img(src_view.dimensions()); + + for (std::size_t i = 0; i < src_view.num_channels(); i++) + { + morph_impl(nth_channel_view(src_view, i), nth_channel_view(view(intermediate_img), i), + ker_mat, identifier); + } + copy_pixels(view(intermediate_img), dst_view); +} + +/// \brief Calculates the difference between pixel values of first image_view +/// and second image_view. +/// \param src_view1 - First parameter for subtraction of views. +/// \param src_view2 - Second parameter for subtraction of views. +/// \param diff_view - View containing result of the subtraction of second view from +/// the first view. +/// \tparam SrcView type of source/Input images used for subtraction. +/// \tparam DiffView type of image view containing the result of subtraction. +template +void difference_impl(SrcView const& src_view1, SrcView const& src_view2, DiffView const& diff_view) +{ + for (std::ptrdiff_t view_row = 0; view_row < src_view1.height(); ++view_row) + for (std::ptrdiff_t view_col = 0; view_col < src_view1.width(); ++view_col) + diff_view(view_col, view_row) = + src_view1(view_col, view_row) - src_view2(view_col, view_row); +} + +/// \brief Passes parameter values to the function 'difference_impl' alongwith +/// individual channel views of input images. +/// \param src_view1 - First parameter for subtraction of views. +/// \param src_view2 - Second parameter for subtraction of views. +/// \param diff_view - View containing result of the subtraction of second view from the first view. +/// \tparam SrcView type of source/Input images used for subtraction. +/// \tparam DiffView type of image view containing the result of subtraction. +template +void difference(SrcView const& src_view1, SrcView const& src_view2, DiffView const& diff_view) +{ + gil_function_requires>(); + gil_function_requires>(); + + gil_function_requires::type, typename color_space_type::type>>(); + + for (std::size_t i = 0; i < src_view1.num_channels(); i++) + { + difference_impl(nth_channel_view(src_view1, i), nth_channel_view(src_view2, i), + nth_channel_view(diff_view, i)); + } +} +} // namespace detail + +/// \brief Applies morphological dilation on the input image view using given +/// structuring element. It gives the maximum overlapped value to the pixel +/// overlapping with the center element of structuring element. \param src_view +/// - Source/input image view. +/// \param int_op_view - view for writing output and performing intermediate operations. +/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for +/// applying dilation. +/// \param iterations - Specifies the number of times dilation is to be applied on the input image +/// view. +/// \tparam SrcView type of source image, models gil::ImageViewConcept. +/// \tparam IntOpView type of output image, models gil::MutableImageViewConcept. +/// \tparam Kernel type of structuring element. +template +void dilate(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat, + int iterations) +{ + copy_pixels(src_view, int_op_view); + for (int i = 0; i < iterations; ++i) + morph(int_op_view, int_op_view, ker_mat, detail::morphological_operation::dilation); +} + +/// \brief Applies morphological erosion on the input image view using given +/// structuring element. It gives the minimum overlapped value to the pixel +/// overlapping with the center element of structuring element. +/// \param src_view - Source/input image view. +/// \param int_op_view - view for writing output and performing intermediate operations. +/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for +/// applying erosion. +/// \param iterations - Specifies the number of times erosion is to be applied on the input +/// image view. +/// \tparam SrcView type of source image, models gil::ImageViewConcept. +/// \tparam IntOpView type of output image, models gil::MutableImageViewConcept. +/// \tparam Kernel type of structuring element. +template +void erode(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat, + int iterations) +{ + copy_pixels(src_view, int_op_view); + for (int i = 0; i < iterations; ++i) + morph(int_op_view, int_op_view, ker_mat, detail::morphological_operation::erosion); +} + +/// \brief Performs erosion and then dilation on the input image view . This +/// operation is utilized for removing noise from images. +/// \param src_view - Source/input image view. +/// \param int_op_view - view for writing output and performing intermediate operations. +/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for +/// applying the opening operation. +/// \tparam SrcView type of source image, models gil::ImageViewConcept. +/// \tparam IntOpView type of output image, models gil::MutableImageViewConcept. +/// \tparam Kernel type of structuring element. +template +void opening(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat) +{ + erode(src_view, int_op_view, ker_mat, 1); + dilate(int_op_view, int_op_view, ker_mat, 1); +} + +/// \brief Performs dilation and then erosion on the input image view which is +/// exactly opposite to the opening operation . Closing operation can be +/// utilized for closing small holes inside foreground objects. +/// \param src_view - Source/input image view. +/// \param int_op_view - view for writing output and performing intermediate operations. +/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for +/// applying the closing operation. +/// \tparam SrcView type of source image, models gil::ImageViewConcept. +/// \tparam IntOpView type of output image, models gil::MutableImageViewConcept. +/// \tparam Kernel type of structuring element. +template +void closing(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat) +{ + dilate(src_view, int_op_view, ker_mat, 1); + erode(int_op_view, int_op_view, ker_mat, 1); +} + +/// \brief Calculates the difference between image views generated after +/// applying dilation dilation and erosion on an image . The resultant image +/// will look like the outline of the object(s) present in the image. +/// \param src_view - Source/input image view. +/// \param dst_view - Destination view which will store the final result of morphological +/// gradient operation. +/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which +/// will be used for applying the morphological gradient operation. +/// \tparam SrcView type of source image, models gil::ImageViewConcept. +/// \tparam DstView type of output image, models gil::MutableImageViewConcept. +/// \tparam Kernel type of structuring element. +template +void morphological_gradient(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gil::image int_dilate(src_view.dimensions()), + int_erode(src_view.dimensions()); + dilate(src_view, view(int_dilate), ker_mat, 1); + erode(src_view, view(int_erode), ker_mat, 1); + difference(view(int_dilate), view(int_erode), dst_view); +} + +/// \brief Calculates the difference between input image view and the view +/// generated by opening operation on the input image view. +/// \param src_view - Source/input image view. +/// \param dst_view - Destination view which will store the final result of top hat operation. +/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for +/// applying the top hat operation. +/// \tparam SrcView type of source image, models gil::ImageViewConcept. +/// \tparam DstView type of output image, models gil::MutableImageViewConcept. +/// \tparam Kernel type of structuring element. +template +void top_hat(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gil::image int_opening(src_view.dimensions()); + opening(src_view, view(int_opening), ker_mat); + difference(src_view, view(int_opening), dst_view); +} + +/// \brief Calculates the difference between closing of the input image and +/// input image. +/// \param src_view - Source/input image view. +/// \param dst_view - Destination view which will store the final result of black hat operation. +/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's +/// which will be used for applying the black hat operation. +/// \tparam SrcView type of source image, models gil::ImageViewConcept. +/// \tparam DstView type of output image, models gil::MutableImageViewConcept. +/// \tparam Kernel type of structuring element. +template +void black_hat(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat) +{ + using namespace boost::gil; + gil::image int_closing(src_view.dimensions()); + closing(src_view, view(int_closing), ker_mat); + difference(view(int_closing), src_view, dst_view); +} +/// @} +}} // namespace boost::gil +#endif // BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index 2fc51c1d4a..1581fd3075 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -9,7 +9,8 @@ foreach(_name threshold_binary threshold_truncate - threshold_otsu) + threshold_otsu + morphology) 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 a67c4d9f21..acd74d08f2 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -22,3 +22,4 @@ run median_filter.cpp ; run anisotropic_diffusion.cpp ; run hough_line_transform.cpp ; run hough_circle_transform.cpp ; +run morphology.cpp ; diff --git a/test/core/image_processing/morphology.cpp b/test/core/image_processing/morphology.cpp new file mode 100644 index 0000000000..b57f605723 --- /dev/null +++ b/test/core/image_processing/morphology.cpp @@ -0,0 +1,136 @@ +// +// Copyright 2021 Prathamesh Tagore +// +// 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 + +namespace gil = boost::gil; + +// This function helps us fill pixels of a view given as 2nd argument with +// elements of the vector given as 1st argument. +void pixel_fill(std::vector>& original_binary_vector, + boost::gil::gray8_image_t& original_img) +{ + for (std::ptrdiff_t view_row = 0; view_row < view(original_img).height(); ++view_row) + { + for (std::ptrdiff_t view_col = 0; view_col < view(original_img).width(); ++view_col) + { + view(original_img)(view_col, view_row) = + gil::gray8_pixel_t(original_binary_vector[view_row][view_col]); + } + } +} + +int main() +{ + std::vector> original_binary_vector{ + {0, 0, 0, 0, 0, 0}, {0, 0, 127, 144, 143, 0}, {0, 0, 128, 0, 142, 0}, + {0, 0, 129, 0, 141, 0}, {0, 0, 130, 140, 139, 0}, {0, 0, 131, 0, 0, 0}, + {0, 0, 132, 137, 136, 138}, {0, 0, 133, 134, 135, 0}}; + std::vector> orig_dil_imp{ + {255, 100, 100, 100}, {100, 100, 100, 100}, {100, 100, 100, 100}}; + // All vectors defined below will be used for creating expected image views + // which are supposed to match the views obtained after applying morphological + // operations. + std::vector> exp_dil{ + {0, 127, 144, 144, 144, 143}, {0, 128, 144, 144, 144, 143}, {0, 129, 144, 144, 144, 143}, + {0, 130, 140, 142, 142, 142}, {0, 131, 140, 141, 141, 141}, {0, 132, 140, 140, 140, 139}, + {0, 133, 137, 137, 138, 138}, {0, 133, 137, 137, 138, 138}}; + // Following vector intends to check result of dilation operation when it is + // applied 2 times on the original image. + std::vector> exp_dil_iter2{ + {128, 144, 144, 144, 144, 144}, {129, 144, 144, 144, 144, 144}, + {130, 144, 144, 144, 144, 144}, {131, 144, 144, 144, 144, 144}, + {132, 140, 142, 142, 142, 142}, {133, 140, 141, 141, 141, 141}, + {133, 140, 140, 140, 140, 140}, {133, 137, 137, 138, 138, 138}}; + std::vector> exp_er{ + {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, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 132, 0, 0}}; + // Following vector intends to check result of erosion operation when it is + // applied 2 times on the original image. + std::vector> exp_er_iter2{ + {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, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}}; + std::vector> exp_opening{ + {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, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 132, 132, 132, 0}, {0, 0, 132, 132, 132, 0}}; + std::vector> exp_closing{ + {0, 0, 127, 144, 143, 143}, {0, 0, 127, 144, 143, 143}, {0, 0, 128, 140, 142, 142}, + {0, 0, 129, 140, 141, 141}, {0, 0, 130, 140, 139, 139}, {0, 0, 131, 137, 137, 138}, + {0, 0, 132, 137, 137, 138}, {0, 0, 133, 137, 137, 138}}; + std::vector> exp_mg{{0, 127, 144, 144, 144, 143}, {0, 128, 144, 144, 144, 143}, + {0, 129, 144, 144, 144, 143}, {0, 130, 140, 142, 142, 142}, + {0, 131, 140, 141, 141, 141}, {0, 132, 140, 140, 140, 139}, + {0, 133, 137, 137, 138, 138}, {0, 133, 137, 5, 138, 138}}; + std::vector> exp_top_hat{{0, 0, 0, 0, 0, 0}, {0, 0, 127, 144, 143, 0}, + {0, 0, 128, 0, 142, 0}, {0, 0, 129, 0, 141, 0}, + {0, 0, 130, 140, 139, 0}, {0, 0, 131, 0, 0, 0}, + {0, 0, 0, 5, 4, 138}, {0, 0, 1, 2, 3, 0}}; + std::vector> exp_black_hat{ + {0, 0, 127, 144, 143, 143}, {0, 0, 0, 0, 0, 143}, {0, 0, 0, 140, 0, 142}, + {0, 0, 0, 140, 0, 141}, {0, 0, 0, 0, 0, 139}, {0, 0, 0, 137, 137, 138}, + {0, 0, 0, 0, 1, 0}, {0, 0, 0, 3, 2, 138}}; + std::vector> exp_dil_imp{ + {255, 255, 100, 100}, {255, 255, 100, 100}, {100, 100, 100, 100}}; + + gil::gray8_image_t original_img(6, 8), obtained_dilation(6, 8), expected_dilation(6, 8); + gil::gray8_image_t obtained_erosion(6, 8), expected_erosion(6, 8); + gil::gray8_image_t obtained_opening(6, 8), expected_opening(6, 8); + gil::gray8_image_t obtained_closing(6, 8), expected_closing(6, 8); + gil::gray8_image_t obtained_mg(6, 8), expected_mg(6, 8); + gil::gray8_image_t obtained_top_hat(6, 8), expected_top_hat(6, 8); + gil::gray8_image_t obtained_black_hat(6, 8), expected_black_hat(6, 8); + gil::gray8_image_t obtained_dil_iter2(6, 8), expected_dil_iter2(6, 8); + gil::gray8_image_t obtained_er_iter2(6, 8), expected_er_iter2(6, 8); + gil::gray8_image_t obtained_imp_dil(4, 3), expected_imp_dil(4, 3), original_imp_dil(4, 3); + + std::vector ker_vec(9, 1.0f); // Structuring element + gil::detail::kernel_2d ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1); + + pixel_fill(original_binary_vector, original_img); + pixel_fill(exp_dil, expected_dilation); + pixel_fill(exp_er, expected_erosion); + pixel_fill(exp_opening, expected_opening); + pixel_fill(exp_closing, expected_closing); + pixel_fill(exp_mg, expected_mg); + pixel_fill(exp_top_hat, expected_top_hat); + pixel_fill(exp_black_hat, expected_black_hat); + pixel_fill(exp_dil_iter2, expected_dil_iter2); + pixel_fill(orig_dil_imp, original_imp_dil); + pixel_fill(exp_dil_imp, expected_imp_dil); + pixel_fill(exp_er_iter2, expected_er_iter2); + + // Different morphological operations are applied on the same initial image to + // obtain results of our implementation which are then compared with expected + // results. + gil::dilate(view(original_img), view(obtained_dilation), ker_mat, 1); + gil::erode(view(original_img), view(obtained_erosion), ker_mat, 1); + gil::opening(view(original_img), view(obtained_opening), ker_mat); + gil::closing(view(original_img), view(obtained_closing), ker_mat); + gil::morphological_gradient(view(original_img), view(obtained_mg), ker_mat); + gil::top_hat(view(original_img), view(obtained_top_hat), ker_mat); + gil::black_hat(view(original_img), view(obtained_black_hat), ker_mat); + gil::dilate(view(original_imp_dil), view(obtained_imp_dil), ker_mat, 1); + gil::dilate(view(original_img), view(obtained_dil_iter2), ker_mat, 2); + gil::erode(view(original_img), view(obtained_er_iter2), ker_mat, 2); + + // Testing obtained results with expected results. + BOOST_TEST(gil::equal_pixels(gil::view(obtained_dilation), gil::view(expected_dilation))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_erosion), gil::view(expected_erosion))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_opening), gil::view(expected_opening))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_closing), gil::view(expected_closing))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_mg), gil::view(expected_mg))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_top_hat), gil::view(expected_top_hat))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_black_hat), gil::view(expected_black_hat))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_imp_dil), gil::view(expected_imp_dil))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_dil_iter2), gil::view(expected_dil_iter2))); + BOOST_TEST(gil::equal_pixels(gil::view(obtained_er_iter2), gil::view(expected_er_iter2))); + + return boost::report_errors(); +} From a8cb364492667b9a6441a6538b458aace64513c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Fri, 19 Feb 2021 22:13:43 +0100 Subject: [PATCH 040/193] doc: Add Compiling section with C++ version and compilers support Clarification suggested in https://github.com/boostorg/website/pull/562 following discussion about https://github.com/boostorg/gil/pull/526 --- doc/installation.rst | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/doc/installation.rst b/doc/installation.rst index 175d1d4007..d810645159 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -1,9 +1,9 @@ Installation ============ -The latest version of GIL can be downloaded from https://github.com/boostorg/gil. +The latest version of Boost.GIL can be downloaded from https://github.com/boostorg/gil. -The GIL is a header-only library. Meaning, it consists of header files only, +The Boost.GIL is a header-only library. Meaning, it consists of header files only, it does not require Boost to be built and it does not require any libraries to link against. @@ -13,5 +13,22 @@ to link against. which requires client libraries implementing popular image formats like libpng, libjpeg, etc. -In order to use GIL, including ``boost/gil.hpp`` and telling your compiler +In order to use Boost.GIL, including ``boost/gil.hpp`` and telling your compiler where to find Boost and GIL headers should be sufficient for most projects. + +Compiling +--------- + +The Boost.GIL library source code should successfully compile with any +compiler with complete C++11 support. + +.. note:: + + We are planning to drop support for require C++14 support in Boost 1.76 or later, + or selectively drop support for GCC 5 due to its issues with inheriting constructors, + see `discussion for PR #526 `_. + +For the actual list of currently tested compilers, check results of the library CI +builds linked from the `README.md `_ +or inspect the CI services configuration files in the `develop branch `_ +of the library repository. From a82af6d25de304bd7482fdfece3b6f775f2832b9 Mon Sep 17 00:00:00 2001 From: Avinal Kumar Date: Sun, 28 Feb 2021 14:24:56 +0530 Subject: [PATCH 041/193] ci: Add Codecov to GitHub Actions (#564) Delete coverage.sh as unused Closes #565 --- .ci/coverage.sh | 7 ----- .github/workflows/coverage.yml | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) delete mode 100644 .ci/coverage.sh create mode 100644 .github/workflows/coverage.yml diff --git a/.ci/coverage.sh b/.ci/coverage.sh deleted file mode 100644 index 42ff3e703f..0000000000 --- a/.ci/coverage.sh +++ /dev/null @@ -1,7 +0,0 @@ -#! /bin/bash -set -e -lcov --directory bin.v2 --capture --no-external --directory $(pwd) --output-file coverage.info > /dev/null 2>&1 -lcov --extract coverage.info $(pwd)'/boost/gil/*' --output-file coverage.info > /dev/null -lcov --list coverage.info -cd libs/gil -bash <(curl -s https://codecov.io/bash) -f ../../coverage.info || echo "Codecov did not collect coverage reports" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000000..c538be936d --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,55 @@ +name: Code Coverage + +on: + pull_request: + push: + branches: + - master + - develop + +env: + LIBRARY: gil + UBSAN_OPTIONS: print_stacktrace=1 + +jobs: + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: install packages + run: | + sudo apt update + sudo apt install g++ libpng-dev libjpeg-dev libtiff5-dev libraw-dev lcov python -y + + - name: Setup Boost + run: | + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + ./bootstrap.sh + ./b2 -d0 headers + + - name: Create user-config.jam + run: | + echo "using gcc : : g++ ;" > ~/user-config.jam + + - name: Run tests + run: | + cd ../boost-root + ./b2 -j3 libs/$LIBRARY/test coverage=on toolset=gcc cxxstd=11 variant=debug + lcov --directory bin.v2 --capture --no-external --directory $(pwd) --output-file coverage.info > /dev/null 2>&1 + lcov --extract coverage.info $(pwd)'/boost/gil/*' --output-file coverage.info > /dev/null + lcov --list coverage.info + + - name: Upload to Codecov + uses: codecov/codecov-action@v1.2.1 + with: + files: ../boost-root/coverage.info + \ No newline at end of file From 33ab3cfc1034b0a67526966b3c126faa912624d2 Mon Sep 17 00:00:00 2001 From: Pranam Lashkari Date: Mon, 1 Mar 2021 04:46:59 +0530 Subject: [PATCH 042/193] added missing header guards (#568) added missing header guards in --- include/boost/gil/image_processing/diffusion.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/boost/gil/image_processing/diffusion.hpp b/include/boost/gil/image_processing/diffusion.hpp index 0c1a3dce75..eedd50ebb8 100644 --- a/include/boost/gil/image_processing/diffusion.hpp +++ b/include/boost/gil/image_processing/diffusion.hpp @@ -1,10 +1,15 @@ // // Copyright 2020 Olzhas Zhumabek +// Copyright 2021 Pranam Lashkari // // 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_DIFFUSION_HPP +#define BOOST_GIL_IMAGE_PROCESSING_DIFFUSION_HPP + #include "boost/gil/detail/math.hpp" #include #include @@ -419,3 +424,5 @@ void anisotropic_diffusion(const InputView& input, const OutputView& output, uns } }} // namespace boost::gil + +#endif From ec25950aff8b953ce359cba95f711a90e3708112 Mon Sep 17 00:00:00 2001 From: Pranam Lashkari Date: Mon, 1 Mar 2021 14:42:33 +0530 Subject: [PATCH 043/193] added missing copyright info (#569) added missing copyright info --- include/boost/gil/image_processing/hessian.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/boost/gil/image_processing/hessian.hpp b/include/boost/gil/image_processing/hessian.hpp index f88c153607..f37c0ffdd5 100644 --- a/include/boost/gil/image_processing/hessian.hpp +++ b/include/boost/gil/image_processing/hessian.hpp @@ -1,3 +1,10 @@ +// +// Copyright 2019 Olzhas Zhumabek +// Copyright 2021 Scramjet911 <36035352+Scramjet911@users.noreply.github.com> +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + #ifndef BOOST_GIL_IMAGE_PROCESSING_HESSIAN_HPP #define BOOST_GIL_IMAGE_PROCESSING_HESSIAN_HPP From ad007ab92127daab26596524402e89ffbf386ccd Mon Sep 17 00:00:00 2001 From: Avinal Kumar <185067@nith.ac.in> Date: Mon, 1 Mar 2021 16:16:27 +0530 Subject: [PATCH 044/193] ci: Add Codecov badge and codecov.yml file (#567) --- README.md | 8 ++++---- codecov.yml | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 codecov.yml diff --git a/README.md b/README.md index 62895dae58..50a0cbe349 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://bintray.com/bincrafters/public-conan/boost_gil%3Abincrafters) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://github.com/Microsoft/vcpkg/tree/master/ports/boost-gil) -Documentation | GitHub Actions | Azure Pipelines | CircleCI | Regression ---------------|----------------|-----------------|-----------------|------------ -[![develop](https://img.shields.io/badge/doc-develop-blue.svg)](https://boostorg.github.io/gil/develop/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/gil/actions?query=branch:develop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h/branch/develop?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/develop) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=develop)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=develop) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/develop.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/develop) | [![gil](https://img.shields.io/badge/gil-develop-blue.svg)](http://www.boost.org/development/tests/develop/developer/gil.html) -[![master](https://img.shields.io/badge/doc-master-blue.svg)](https://boostorg.github.io/gil/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=master)](https://github.com/boostorg/gil/actions?query=branch:master) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/master) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=master)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=master) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/master.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/master) | [![gil](https://img.shields.io/badge/gil-master-blue.svg)](http://www.boost.org/development/tests/master/developer/gil.html) +Documentation | GitHub Actions | AppVeyor | Azure Pipelines | CircleCI | Regression | Codecov +--------------|----------------|------------|-----------------|-----------------|------------|---------- +[![develop](https://img.shields.io/badge/doc-develop-blue.svg)](https://boostorg.github.io/gil/develop/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/gil/actions?query=branch:develop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h/branch/develop?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/develop) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=develop)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=develop) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/develop.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/develop) | [![gil](https://img.shields.io/badge/gil-develop-blue.svg)](http://www.boost.org/development/tests/develop/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/develop) +[![master](https://img.shields.io/badge/doc-master-blue.svg)](https://boostorg.github.io/gil/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=master)](https://github.com/boostorg/gil/actions?query=branch:master) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/master) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=master)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=master) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/master.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/master) | [![gil](https://img.shields.io/badge/gil-master-blue.svg)](http://www.boost.org/development/tests/master/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/master) # Boost.GIL diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..7359abc758 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,7 @@ +comment: + layout: "diff" + behavior: default + branches: + - master + - develop + require_changes: true From 093117d5650cb4f8f277dd73a00888c488447f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Ferdinand=20Rivera=20Morell?= Date: Mon, 1 Mar 2021 14:13:36 -0600 Subject: [PATCH 045/193] Repoint B2 refs to new non-boostorg home. (#570) Also, relabel Boost.Build to the current B2 name. --- CONTRIBUTING.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5492bd7e52..91613c08c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ please follow the workflow explained in this document. - [5. Update your pull request](#5-update-your-pull-request) - [Development](#development) - [Install dependencies](#install-dependencies) - - [Using Boost.Build](#using-boostbuild) + - [Using B2](#using-boostbuild) - [Using CMake](#using-cmake) - [Running clang-tidy](#running-clang-tidy) - [Guidelines](#guidelines) @@ -35,7 +35,7 @@ please follow the workflow explained in this document. it may be a good idea to skim through the [Boost Getting Started](https://www.boost.org/more/getting_started/index.html) chapters, especially if you are going to use - [Boost.Build](https://boostorg.github.io/build/) for the first time. + [B2](https://www.bfgroup.xyz/b2/) for the first time. ## Pull Requests @@ -124,7 +124,7 @@ The preparation involves the following steps: git submodule update --init --recursive --jobs 8 ``` -3. Build the `b2` driver program for Boost.Build engine. +3. Build the `b2` driver program for B2 engine. ```shell ./bootstrap.sh @@ -311,15 +311,15 @@ Boost.GIL is a [header-only library](https://en.wikipedia.org/wiki/Header-only) which does not require sources compilation. Only test runners and [example](example/README.md) programs have to be compiled. -By default, Boost.GIL uses Boost.Build to build all the executables. +By default, Boost.GIL uses B2 to build all the executables. We also provide configuration for two alternative build systems: - [CMake](https://cmake.org) **NOTE:** The CMake is optional and the corresponding build configurations -for Boost.GIL do not offer equivalents for all Boost.Build features. -Most important difference to recognise is that Boost.Build will automatically +for Boost.GIL do not offer equivalents for all B2 features. +Most important difference to recognise is that B2 will automatically build any other Boost libraries required by Boost.GIL as dependencies. ### Install dependencies @@ -333,9 +333,9 @@ sudo apt-get install libjpeg-dev libpng-dev libtiff5-dev libraw-dev **TIP:** On Windows, use vcpkg with `user-config.jam` configuration provided in [example/b2/user-config-windows-vcpkg.jam](example/b2/). -### Using Boost.Build +### Using B2 -The [b2 invocation](https://boostorg.github.io/build/manual/develop/index.html#bbv2.overview.invocation) +The [b2 invocation](https://www.bfgroup.xyz/b2/manual/release/index.html#bbv2.overview.invocation) explains available options like `toolset`, `variant` and others. Simply, just execute `b2` to run all tests built using default From f18445f90ba0ae05877d659c9fb369ffb55afa95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Ferdinand=20Rivera=20Morell?= Date: Mon, 1 Mar 2021 14:15:02 -0600 Subject: [PATCH 046/193] Repoint B2 refs to new non-boostorg home. (#571) --- example/b2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/b2/README.md b/example/b2/README.md index e4f56aaf8a..c847a26015 100644 --- a/example/b2/README.md +++ b/example/b2/README.md @@ -8,7 +8,7 @@ users who wish to run complete build of Boost.GIL tests and examples. Copy any of the provided user configuration files to `%HOME%\user-config.jam` on Windows or `$HOME/user-config.jam` on Linux. -Refer to [Boost.Build User Manual](https://boostorg.github.io/build/) +Refer to [B2 User Manual](https://www.bfgroup.xyz/b2/manual/release/index.html) for more details about use of configuration files. ## Examples From 0778069b8e97fed6138fbd5dab6e5bb5e9a2ef37 Mon Sep 17 00:00:00 2001 From: Gopi Krishna Menon Date: Tue, 2 Mar 2021 15:32:07 +0530 Subject: [PATCH 047/193] Switch docs deployment from Travis CI to GitHub Actions (#563) --- .github/actions/docs-prerequisites/action.yml | 7 ++++ .../actions/generate-publish-doc/action.yml | 17 ++++++++ .../generate-publish-doc/upload-script.sh | 40 +++++++++++++++++++ .github/actions/setup-boost/action.yml | 17 ++++++++ .github/workflows/docs.yaml | 17 ++++++++ 5 files changed, 98 insertions(+) create mode 100644 .github/actions/docs-prerequisites/action.yml create mode 100644 .github/actions/generate-publish-doc/action.yml create mode 100644 .github/actions/generate-publish-doc/upload-script.sh create mode 100644 .github/actions/setup-boost/action.yml create mode 100644 .github/workflows/docs.yaml diff --git a/.github/actions/docs-prerequisites/action.yml b/.github/actions/docs-prerequisites/action.yml new file mode 100644 index 0000000000..bc5a6a426c --- /dev/null +++ b/.github/actions/docs-prerequisites/action.yml @@ -0,0 +1,7 @@ +name: 'Get Docs Prerequisites' +description: 'Downloads all the necessary packages for building documentation' +runs: + using: composite + steps: + - run: sudo apt-get install doxygen python3 python3-pip python3-setuptools python3-sphinx + shell: bash diff --git a/.github/actions/generate-publish-doc/action.yml b/.github/actions/generate-publish-doc/action.yml new file mode 100644 index 0000000000..d794f43e59 --- /dev/null +++ b/.github/actions/generate-publish-doc/action.yml @@ -0,0 +1,17 @@ +name: 'Generate Doc' +description: 'Runs b2 on lib/gil/doc to generate documentation' +inputs: + github_token: + description: 'Github Token for Access' + required: true +runs: + using: composite + steps: + - run: | + echo "using doxygen ;" > ~/user-config.jam + cd ../boost-root/libs + ../b2 gil/doc + cd gil + chmod +x $GITHUB_WORKSPACE/.github/actions/generate-publish-doc/upload-script.sh + $GITHUB_WORKSPACE/.github/actions/generate-publish-doc/upload-script.sh ${{inputs.github_token}} + shell: bash diff --git a/.github/actions/generate-publish-doc/upload-script.sh b/.github/actions/generate-publish-doc/upload-script.sh new file mode 100644 index 0000000000..6883c198fc --- /dev/null +++ b/.github/actions/generate-publish-doc/upload-script.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e # Exit with Non Zero exit Code + +# Save some useful information +SHA=`git rev-parse --verify HEAD` + +mkdir temp-doc +cd temp-doc +git init + +git config user.name "Github Action Upload Docs" +git config user.email "Github Action Upload Docs" + +git remote add upstream "https://$1@github.com/boostorg/gil.git" + +git fetch upstream +git switch gh-pages + + +if [ "${GITHUB_REF##*/}" = develop ]; then + rm -r develop + mkdir -p develop/doc + cp ../index.html develop/ + cp ../doc/index.html develop/doc + cp -a ../doc/html develop/doc/ +else + rm index.html + rm -r html + cp ../doc/index.html . + cp -r ../doc/html . +fi + + +git add . + +# Commit the new version. +git commit -m "Deploy to GitHub Pages: ${SHA}" + +# Now that we're all set up, we can push. +git push -q upstream HEAD:gh-pages diff --git a/.github/actions/setup-boost/action.yml b/.github/actions/setup-boost/action.yml new file mode 100644 index 0000000000..89ba925f37 --- /dev/null +++ b/.github/actions/setup-boost/action.yml @@ -0,0 +1,17 @@ +name: 'Setup-Boost' +description: 'Downloads and sets up the necessary dependencies of Boost' +runs: + using: composite + steps: + - run: | + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/gil + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" gil + ./bootstrap.sh + ./b2 -d0 headers + shell: bash diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 0000000000..376c42360b --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,17 @@ +name: Docs-Build +on: + push: + branches: + - master + - develop + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/docs-prerequisites + - uses: ./.github/actions/setup-boost + - uses: ./.github/actions/generate-publish-doc + with: + github_token: ${{ secrets.GIL_TEST_TOKEN }} From 8bd2413127237e6fdb3a903ea662bd5e0dfc1b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 2 Mar 2021 19:25:11 +0100 Subject: [PATCH 048/193] ci: Remove GCC 5 on GitHub Actions and Azure Pipelines (#572) Required by #526 See also https://github.com/boostorg/website/pull/562 Disable system.debug on AzP --- .azure-pipelines.yml | 11 +++++++++-- .github/workflows/ci.yml | 7 ------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 524365e51f..bf35a61860 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -5,7 +5,7 @@ # (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) # variables: - system.debug: true + #system.debug: true configuration: release trigger: @@ -15,10 +15,13 @@ trigger: - ml/* jobs: - - job: 'ubuntu1604_gcc5_cxx11_cmake' + - job: 'ubuntu1604_gcc6_cxx14_cmake' pool: vmImage: 'ubuntu-16.04' steps: + - template: .ci/azure-pipelines/steps-install-gcc.yml + parameters: + major_version: '6' - script: which g++ && g++ --version displayName: 'Check GCC' - template: .ci/azure-pipelines/steps-check-cmake.yml @@ -28,12 +31,16 @@ jobs: displayName: 'Install dependencies' - template: .ci/azure-pipelines/steps-install-boost.yml - template: .ci/azure-pipelines/steps-cmake-build-and-test.yml + parameters: + cxxver: '14' - job: 'ubuntu1604_gcc8_cxx14_cmake' pool: vmImage: 'ubuntu-16.04' steps: - template: .ci/azure-pipelines/steps-install-gcc.yml + parameters: + major_version: '8' - script: which g++ && g++ --version displayName: 'Check GCC' - template: .ci/azure-pipelines/steps-check-cmake.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 880ca948ed..3c3f063b6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,13 +18,6 @@ jobs: fail-fast: false matrix: include: - - toolset: gcc-4.9 - cxxstd: "11" - os: ubuntu-16.04 - install: g++-4.9 - - toolset: gcc-5 - cxxstd: "11,14,1z" - os: ubuntu-16.04 - toolset: gcc-6 cxxstd: "11,14,1z" os: ubuntu-16.04 From 745d033ef2f382dea338851cfa6b2668f1e52905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Fri, 5 Mar 2021 20:57:59 +0100 Subject: [PATCH 049/193] ci: Use GitHub Actions automatic GITHUB_TOKEN secret in docs workflow --- .github/workflows/docs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 376c42360b..171f44e7f5 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v2 - uses: ./.github/actions/docs-prerequisites - - uses: ./.github/actions/setup-boost + - uses: ./.github/actions/setup-boost - uses: ./.github/actions/generate-publish-doc with: - github_token: ${{ secrets.GIL_TEST_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} From 392ac4996d49d7ea958426438775d291b689af07 Mon Sep 17 00:00:00 2001 From: Gaurav kumar <45493322+gkumar28@users.noreply.github.com> Date: Tue, 9 Mar 2021 10:26:21 +0530 Subject: [PATCH 050/193] Rotation of image by any arbitrary angle from its center (#565) * basic rotation function for theta between +/- 180 deg * added scaling matrix for same dimensions and added bound checks for theta --- .../boost/gil/extension/numeric/affine.hpp | 62 ++++++++++++++++++- test/extension/numeric/matrix3x2.cpp | 16 +++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/include/boost/gil/extension/numeric/affine.hpp b/include/boost/gil/extension/numeric/affine.hpp index 26054149a9..7b590e64a6 100644 --- a/include/boost/gil/extension/numeric/affine.hpp +++ b/include/boost/gil/extension/numeric/affine.hpp @@ -108,6 +108,66 @@ boost::gil::matrix3x2 inverse(boost::gil::matrix3x2 m) return res; } +/// \fn gil::matrix3x2 center_rotate +/// \tparam T Data type for source image dimensions +/// \tparam F Data type for angle through which image is to be rotated +/// @param dims dimensions of source image +/// @param rads angle through which image is to be rotated +/// @return A transformation matrix for rotating the source image about its center +/// \brief rotates an image from its center point +/// using consecutive affine transformations. +template +boost::gil::matrix3x2 center_rotate(boost::gil::point dims,F rads) +{ + const F PI = F(3.141592653589793238); + const F c_theta = std::abs(std::cos(rads)); + const F s_theta = std::abs(std::sin(rads)); + + // Bound checks for angle rads + while(rads + PI < 0) + { + rads = rads + PI; + } + + while(rads > PI) + { + rads = rads - PI; + } + + // Basic Rotation Matrix + boost::gil::matrix3x2 rotate = boost::gil::matrix3x2::get_rotate(rads); + + // Find distance for translating the image into view + boost::gil::matrix3x2 translation(0,0,0,0,0,0); + if(rads > 0) + { + translation.b = s_theta; + } + else + { + translation.c = s_theta; + } + + if(std::abs(rads) > PI/2) + { + translation.a = c_theta; + translation.d = c_theta; + } + + // To bring the complete image into view + boost::gil::matrix3x2 translate = + boost::gil::matrix3x2::get_translate(-1 * dims * translation); + + // To fit inside the source dimensions + boost::gil::matrix3x2 scale = + boost::gil::matrix3x2::get_scale( + s_theta * dims.y / dims.x + c_theta , + s_theta * dims.x / dims.y + c_theta + ); + + return scale * translate * rotate; +} + }} // namespace boost::gil -#endif +#endif \ No newline at end of file diff --git a/test/extension/numeric/matrix3x2.cpp b/test/extension/numeric/matrix3x2.cpp index d3c34f4e48..1f0ae8f67d 100644 --- a/test/extension/numeric/matrix3x2.cpp +++ b/test/extension/numeric/matrix3x2.cpp @@ -201,6 +201,21 @@ void test_matrix3x2_inverse() BOOST_TEST_WITH(p.y, p2.y, with_tolerance(1e-9)); } +void test_matrix3x2_center_rotate() +{ + gil::point dimension(100.0,100.0); + gil::matrix3x2 m1; + + m1 = gil::center_rotate(dimension, HALF_PI); + + BOOST_TEST_WITH(m1.a , std::cos(HALF_PI) , with_tolerance(1e-9)); + BOOST_TEST_EQ (m1.b , 1); + BOOST_TEST_EQ (m1.c , -1); + BOOST_TEST_WITH(m1.d , std::cos(HALF_PI) , with_tolerance(1e-9)); + BOOST_TEST_EQ (m1.e , 100); + BOOST_TEST_WITH(m1.f , std::cos(HALF_PI) , with_tolerance(1e-9)); +} + int main() { test_matrix3x2_default_constructor(); @@ -215,6 +230,7 @@ int main() test_matrix3x2_get_translate(); test_matrix3x2_transform(); test_matrix3x2_inverse(); + test_matrix3x2_center_rotate(); return ::boost::report_errors(); } From 6da59cc3351e5657275d3a536e0b6e7a1b6ac738 Mon Sep 17 00:00:00 2001 From: Gopi Krishna Menon Date: Wed, 10 Mar 2021 13:46:10 +0530 Subject: [PATCH 051/193] docs: Updated GA workflow to publish with peaceiris/action-gh-pages (#574) --- .github/actions/generate-doc/action.yml | 13 ++++++++++++ .../docs-config.sh} | 20 ++++++------------- .../actions/generate-publish-doc/action.yml | 17 ---------------- .github/workflows/docs.yaml | 7 ++++++- 4 files changed, 25 insertions(+), 32 deletions(-) create mode 100644 .github/actions/generate-doc/action.yml rename .github/actions/{generate-publish-doc/upload-script.sh => generate-doc/docs-config.sh} (51%) delete mode 100644 .github/actions/generate-publish-doc/action.yml diff --git a/.github/actions/generate-doc/action.yml b/.github/actions/generate-doc/action.yml new file mode 100644 index 0000000000..3ab8995f32 --- /dev/null +++ b/.github/actions/generate-doc/action.yml @@ -0,0 +1,13 @@ +name: 'Generate Doc' +description: 'Runs b2 on lib/gil/doc to generate documentation' +runs: + using: composite + steps: + - run: | + echo "using doxygen ;" > ~/user-config.jam + cd ../boost-root/libs + ../b2 gil/doc + cd gil + chmod +x $GITHUB_WORKSPACE/.github/actions/generate-doc/docs-config.sh + $GITHUB_WORKSPACE/.github/actions/generate-doc/docs-config.sh + shell: bash diff --git a/.github/actions/generate-publish-doc/upload-script.sh b/.github/actions/generate-doc/docs-config.sh similarity index 51% rename from .github/actions/generate-publish-doc/upload-script.sh rename to .github/actions/generate-doc/docs-config.sh index 6883c198fc..695d6d0eb0 100644 --- a/.github/actions/generate-publish-doc/upload-script.sh +++ b/.github/actions/generate-doc/docs-config.sh @@ -1,40 +1,32 @@ #!/bin/bash set -e # Exit with Non Zero exit Code -# Save some useful information -SHA=`git rev-parse --verify HEAD` - mkdir temp-doc cd temp-doc + git init -git config user.name "Github Action Upload Docs" -git config user.email "Github Action Upload Docs" -git remote add upstream "https://$1@github.com/boostorg/gil.git" +git remote add upstream "https://github.com/boostorg/gil.git" git fetch upstream git switch gh-pages if [ "${GITHUB_REF##*/}" = develop ]; then + # Only updates develop directory and keeps others intact rm -r develop mkdir -p develop/doc cp ../index.html develop/ cp ../doc/index.html develop/doc cp -a ../doc/html develop/doc/ else + # main branch rm index.html rm -r html cp ../doc/index.html . cp -r ../doc/html . fi - -git add . - -# Commit the new version. -git commit -m "Deploy to GitHub Pages: ${SHA}" - -# Now that we're all set up, we can push. -git push -q upstream HEAD:gh-pages +# Remove version control +rm -rf .git \ No newline at end of file diff --git a/.github/actions/generate-publish-doc/action.yml b/.github/actions/generate-publish-doc/action.yml deleted file mode 100644 index d794f43e59..0000000000 --- a/.github/actions/generate-publish-doc/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: 'Generate Doc' -description: 'Runs b2 on lib/gil/doc to generate documentation' -inputs: - github_token: - description: 'Github Token for Access' - required: true -runs: - using: composite - steps: - - run: | - echo "using doxygen ;" > ~/user-config.jam - cd ../boost-root/libs - ../b2 gil/doc - cd gil - chmod +x $GITHUB_WORKSPACE/.github/actions/generate-publish-doc/upload-script.sh - $GITHUB_WORKSPACE/.github/actions/generate-publish-doc/upload-script.sh ${{inputs.github_token}} - shell: bash diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 171f44e7f5..9a20e7c55f 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -12,6 +12,11 @@ jobs: - uses: actions/checkout@v2 - uses: ./.github/actions/docs-prerequisites - uses: ./.github/actions/setup-boost - - uses: ./.github/actions/generate-publish-doc + - uses: ./.github/actions/generate-doc + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ../boost-root/libs/gil/temp-doc + user_name: 'github-actions[bot]' + user_email: 'github-actions[bot]@users.noreply.github.com' From b8564e256c067275287792a1678a74cf81d4ae08 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 18 Mar 2021 10:53:53 +0100 Subject: [PATCH 052/193] Fix any_image_view::const_t (#526) Use inherited constructors in any_image as well --- .../gil/extension/dynamic_image/any_image.hpp | 18 +-- .../dynamic_image/any_image_view.hpp | 16 +-- test/extension/dynamic_image/CMakeLists.txt | 1 + test/extension/dynamic_image/Jamfile | 1 + .../dynamic_image/any_image_view.cpp | 128 ++++++++++++++++++ 5 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 test/extension/dynamic_image/any_image_view.cpp diff --git a/include/boost/gil/extension/dynamic_image/any_image.hpp b/include/boost/gil/extension/dynamic_image/any_image.hpp index 701fece697..69b674b1f9 100644 --- a/include/boost/gil/extension/dynamic_image/any_image.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image.hpp @@ -89,29 +89,15 @@ template class any_image : public variant2::variant { using parent_t = variant2::variant; + public: using view_t = mp11::mp_rename, any_image_view>; using const_view_t = mp11::mp_rename, any_image_view>; using x_coord_t = std::ptrdiff_t; using y_coord_t = std::ptrdiff_t; using point_t = point; - - any_image() = default; - any_image(any_image const& img) : parent_t((parent_t const&)img) {} - - template - explicit any_image(Image const& img) : parent_t(img) {} - template - any_image(Image&& img) : parent_t(std::move(img)) {} - - template - explicit any_image(Image& img, bool do_swap) : parent_t(img, do_swap) {} - - template - any_image(any_image const& img) - : parent_t((variant2::variant const&)img) - {} + using parent_t::parent_t; any_image& operator=(any_image const& img) { diff --git a/include/boost/gil/extension/dynamic_image/any_image_view.hpp b/include/boost/gil/extension/dynamic_image/any_image_view.hpp index 901fb6fdbe..5d71b588b0 100644 --- a/include/boost/gil/extension/dynamic_image/any_image_view.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image_view.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2020 Samuel Debionne // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -24,10 +25,10 @@ struct dynamic_xy_step_transposed_type; namespace detail { template -struct get_const_t { using type = typename View::const_t; }; +using get_const_t = typename View::const_t; template -struct views_get_const_t : mp11::mp_transform {}; +using views_get_const_t = mp11::mp_transform; // works for both image_view and image struct any_type_get_num_channels @@ -82,16 +83,7 @@ class any_image_view : public variant2::variant using point_t = point; using size_type = std::size_t; - any_image_view() = default; - any_image_view(any_image_view const& view) : parent_t((parent_t const&)view) {} - - template - explicit any_image_view(View const& view) : parent_t(view) {} - - template - any_image_view(any_image_view const& view) - : parent_t((variant2::variant const&)view) - {} + using parent_t::parent_t; any_image_view& operator=(any_image_view const& view) { diff --git a/test/extension/dynamic_image/CMakeLists.txt b/test/extension/dynamic_image/CMakeLists.txt index d2a37bf10c..0e720b5bab 100644 --- a/test/extension/dynamic_image/CMakeLists.txt +++ b/test/extension/dynamic_image/CMakeLists.txt @@ -8,6 +8,7 @@ message(STATUS "Boost.GIL: Configuring tests in test/extension/dynamic_image") foreach(_name any_image + any_image_view subimage_view) set(_test t_ext_dynamic_image_${_name}) set(_target test_ext_dynamic_image_${_name}) diff --git a/test/extension/dynamic_image/Jamfile b/test/extension/dynamic_image/Jamfile index 8cd2d5184d..63189265e4 100644 --- a/test/extension/dynamic_image/Jamfile +++ b/test/extension/dynamic_image/Jamfile @@ -11,4 +11,5 @@ import testing ; alias headers : [ generate_self_contained_headers extension/dynamic_image ] ; run any_image.cpp ; +run any_image_view.cpp ; run subimage_view.cpp ; diff --git a/test/extension/dynamic_image/any_image_view.cpp b/test/extension/dynamic_image/any_image_view.cpp new file mode 100644 index 0000000000..176e7f25b2 --- /dev/null +++ b/test/extension/dynamic_image/any_image_view.cpp @@ -0,0 +1,128 @@ +// +// Copyright 2020 Samuel Debionne +// +// Distributed under 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 + +#include "test_fixture.hpp" +#include "core/image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +void test_any_image_view_nested_types() +{ + BOOST_TEST_TRAIT_SAME(gil::any_image_view::const_t, gil::any_image_view); +} + + +struct test_any_image_view_init_ctor +{ + template + void operator()(Image const&) + { + using image_t = Image; + using view_t = typename Image::view_t; + using any_view_t = gil::any_image_view; + using any_const_view_t = typename any_view_t::const_t; + Image i0(fixture::create_image(4, 4, 128)); + + view_t v0 = gil::view(i0); + any_view_t v1 = v0; + + BOOST_TEST_EQ(v1.dimensions().x, 4); + BOOST_TEST_EQ(v1.dimensions().y, 4); + + any_const_view_t v2 = v0; + + BOOST_TEST_EQ(v2.dimensions().x, 4); + BOOST_TEST_EQ(v2.dimensions().y, 4); + + //any_const_view_t v3 = v1; + } + static void run() + { + boost::mp11::mp_for_each(test_any_image_view_init_ctor{}); + } +}; + +struct test_any_image_view_copy_ctor +{ + template + void operator()(Image const&) + { + using image_t = Image; + using view_t = typename Image::view_t; + using any_view_t = gil::any_image_view; + using any_const_view_t = typename any_view_t::const_t; + Image i0(fixture::create_image(4, 4, 128)); + + view_t v0 = gil::view(i0); + any_view_t v1 = v0; + + BOOST_TEST_EQ(v1.dimensions().x, 4); + BOOST_TEST_EQ(v1.dimensions().y, 4); + + any_const_view_t v2 = v0; + + BOOST_TEST_EQ(v2.dimensions().x, 4); + BOOST_TEST_EQ(v2.dimensions().y, 4); + + //any_const_view_t v3 = v1; + } + static void run() + { + boost::mp11::mp_for_each(test_any_image_view_copy_ctor{}); + } +}; + +struct test_any_image_view_assign_operator +{ + template + void operator()(Image const&) + { + using image_t = Image; + using view_t = typename Image::view_t; + using any_view_t = gil::any_image_view; + using any_const_view_t = typename any_view_t::const_t; + Image i0(fixture::create_image(4, 4, 128)); + + view_t v0 = gil::view(i0); + any_view_t v1; + any_const_view_t v2; + + v1 = v0; + + BOOST_TEST_EQ(v1.dimensions().x, 4); + BOOST_TEST_EQ(v1.dimensions().y, 4); + + v2 = v0; + + BOOST_TEST_EQ(v2.dimensions().x, 4); + BOOST_TEST_EQ(v2.dimensions().y, 4); + + //v2 = v1; + } + static void run() + { + boost::mp11::mp_for_each(test_any_image_view_assign_operator{}); + } +}; + +int main() +{ + test_any_image_view_init_ctor::run(); + test_any_image_view_copy_ctor::run(); + test_any_image_view_assign_operator::run(); + + return ::boost::report_errors(); +} From dc9ba74cb3e7801f7025902796a76172d75517bf Mon Sep 17 00:00:00 2001 From: Debabrata Mandal <32168969+codejaeger@users.noreply.github.com> Date: Thu, 25 Mar 2021 01:49:06 +0530 Subject: [PATCH 053/193] Add docs for histogram (#503) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add docs for histogram * Add docs and make changes: - Made changes suggested in first review - Add docs for relevant files * Rename docs file and correct typos * Change doc for cumulative histogram * Add docs for histogram equalization * Make changes suggested in review * Add docs * Remove docs for algorithms * Move images to test_images Co-authored-by: Mateusz Łoskot --- doc/histogram/create.rst | 42 +++++++ doc/histogram/cumulative.rst | 29 +++++ doc/histogram/extend.rst | 68 ++++++++++++ doc/histogram/extension/index.rst | 12 ++ doc/histogram/extension/overview.rst | 32 ++++++ doc/histogram/extension/std.rst | 45 ++++++++ doc/histogram/fill.rst | 103 ++++++++++++++++++ doc/histogram/index.rst | 20 ++++ doc/histogram/limitations.rst | 6 + doc/histogram/overview.rst | 28 +++++ doc/histogram/stl_compatibility.rst | 6 + doc/histogram/subhistogram.rst | 66 +++++++++++ doc/histogram/utilities.rst | 6 + .../contrast_enhancement/index.rst | 11 ++ .../contrast_enhancement/overview.rst | 15 +++ doc/image_processing/index.rst | 2 + doc/index.rst | 2 + 17 files changed, 493 insertions(+) create mode 100644 doc/histogram/create.rst create mode 100644 doc/histogram/cumulative.rst create mode 100644 doc/histogram/extend.rst create mode 100644 doc/histogram/extension/index.rst create mode 100644 doc/histogram/extension/overview.rst create mode 100644 doc/histogram/extension/std.rst create mode 100644 doc/histogram/fill.rst create mode 100644 doc/histogram/index.rst create mode 100644 doc/histogram/limitations.rst create mode 100644 doc/histogram/overview.rst create mode 100644 doc/histogram/stl_compatibility.rst create mode 100644 doc/histogram/subhistogram.rst create mode 100644 doc/histogram/utilities.rst create mode 100644 doc/image_processing/contrast_enhancement/index.rst create mode 100644 doc/image_processing/contrast_enhancement/overview.rst diff --git a/doc/histogram/create.rst b/doc/histogram/create.rst new file mode 100644 index 0000000000..7df551c63e --- /dev/null +++ b/doc/histogram/create.rst @@ -0,0 +1,42 @@ +.. _create_histogram: + +Create a histogram +================== + +**Method 1** - Using the histogram constructor + +Syntax:: + + histogram + +``Type1`` .. ``TypeN`` correspond to the axis type of the N axes in the histogram + +Example: If we want a 3D histogram of Axis1 of type ``int``, Axis2 of type ``float`` and Axis3 of type ``std::string`` +we would do it this way:: + + histogram h; + +And done. + + +**Method 2** (TODO) - Using make_histogram() + +There is an alternative to create the histogram directly from +a GIL image view. + +This should be the preferred over method-1 when creating +histogram with GIL images, since it creates a histogram with axes configured +to match the GIL image. + +Also it is easier than method-1. + +Syntax:: + + auto h = make_histogram(view(image)); + +where ``image`` could be a ``gray8_image_t``/``rgb8_image_t`` object read from source. + + + + + diff --git a/doc/histogram/cumulative.rst b/doc/histogram/cumulative.rst new file mode 100644 index 0000000000..a769f9ddde --- /dev/null +++ b/doc/histogram/cumulative.rst @@ -0,0 +1,29 @@ +.. _cumulative_histogram: + +Making a cumulative histogram +============================= + +Overview +-------- + +A cumulative histogram is a histogram in which each bin stores the count / frequency of itself +as well as all the bins with keys 'smaller' than the particular bin. +As such, a notion of ordering among its keys should be existant in the histogram. + +The GIL histogram class has the ability to convert itself into its cumulative version. + +Since the container needs to first get an ordering +over the keys a key sorting takes place before calculating the cumulative histogram. + +Example: + + .. code-block:: cpp + + histogram h; + /* + Fill histogram ... + */ + auto h1 = cumulative_histogram(h); + +Tip: *In case you need to store the cumulative histogram elsewhere, consider creating a copy of the histogram +and then call the function*. \ No newline at end of file diff --git a/doc/histogram/extend.rst b/doc/histogram/extend.rst new file mode 100644 index 0000000000..3197af7841 --- /dev/null +++ b/doc/histogram/extend.rst @@ -0,0 +1,68 @@ +.. _extend_support: + +Extending the class +=================== + +.. contents:: + :local: + :depth: 1 + +User defined Axes +----------------- + +In case you need a histogram with an axes of an arbitrary type that is not identified by +the C++ Standard Library, you need to provide a overload for the hashing function that is +used in the histogram class. + +GIL's histogram class uses ``boost::hash_combine`` in a sub routine to generate a hash from +the key. + +So we need to provide an overload of ``boost::hash_combine`` for the purpose. + +For example, let's consider you need a histogram with an axis over class Test. + +.. code-block:: cpp + + // File : ./test.hpp + #include + #include + + struct Test + { + int a{0}; + Test() = default; + Test(int c) : a(c) {} + bool operator==(Test const& other) const + { + return (a == other.a); + } + }; + + namespace boost { + std::size_t hash_value(Test const& t) + { + // Replace with your hashing code + std::hash hasher; + return hasher(t.a); + } + } + +Now lets get to the usage example. + +.. code-block:: cpp + + #include + #include + #include + // Mind the order of include i.e. test.hpp before boost/gil.hpp + + using namespace boost::gil; + + int main() + { + boost::gil::histogram h; + Test t(1); + h(t) = 1; + std::cout< v; + gil::gray8_image_t img; + /* + Fill image ... + */ + gil::fill_histogram(view(img), v, false); + +#. **cumulative_histogram()** + + .. code-block:: cpp + + // Demo for std::vector + std::vector v; + /* + Fill vector... + */ + gil::cumulative_histogram(v); + + + + + + diff --git a/doc/histogram/fill.rst b/doc/histogram/fill.rst new file mode 100644 index 0000000000..cf3f913c6a --- /dev/null +++ b/doc/histogram/fill.rst @@ -0,0 +1,103 @@ +.. _fill_it: + +Fill histogram +============== + +.. contents:: + :local: + :depth: 1 + +Overview +-------- + +We will demonstrate the available options for filling an instance of the `histogram` class with +values that cater from the most simplest to the complex needs that might arise. + +Basic +----- + +#. Use operator() + + **Task** - Add value to a particular cell / key / bin in histogram + + .. code-block:: cpp + + histogram h; + h(1, 2) = 1; + +#. Use operator[] + + This requires to input the indices in a format the histogram internally stores its keys, + which is of ``std::tuple`` due to its simple interface. + + **Task** - Output value of a bin + + .. code-block:: cpp + + histogram h; + h(1, 2) = 1; + h[{1, 2}] += 1; // Note the curly braces reqd. to construct a tuple + std::cout< A; + /* + Fill value in A + */ + histogram B(A), C; + C = A; + +#. Use a GIL image view + + You can also use GIL images to directly fill histograms. + + **Task** - Fill histogram using GIL image view + + .. code-block:: cpp + + gil::gray8_image_t img; + /* + Fill img ... + */ + histogram h; + h.fill(view(img)); + // OR + gil::fill_histogram(view(img), h, false); // false if histogram needs to be cleared before filling + + +Advanced +-------- + +#. Fill histogram using only a few dimensions of image + + **Task** - Make an histogram over Red and Blue channel of an rgb image + + .. code-block:: cpp + + gil::rgb8_image_t img; + /* + Fill img ... + */ + histogram h; + fill_histogram<0, 2>(view(img), h, false); // 0 - red, 1 - green, 2 - blue + +#. Fill histogram using GIL pixel + + **Task** - Fill histogram bin using pixel construct in GIL + + .. code-block:: cpp + + gil::gray8_image_t img; + /* + Fill img ... + */ + histogram h; + gil::for_each_pixel(view(img), [](gil::gray8_pixel_t const& p){ + ++h[h.key_from_pixel(p)]; + }); + diff --git a/doc/histogram/index.rst b/doc/histogram/index.rst new file mode 100644 index 0000000000..ff8cc16bba --- /dev/null +++ b/doc/histogram/index.rst @@ -0,0 +1,20 @@ +Histogram +========= + +The GIL documentation sections listed below are dedicated to describe the +histogram class and functions used in many image processing algorithms. + +.. toctree:: + :maxdepth: 1 + :caption: Table of Contents + + overview + create + fill + subhistogram + cumulative + stl_compatibility + utilities + extend + limitations + extension/index diff --git a/doc/histogram/limitations.rst b/doc/histogram/limitations.rst new file mode 100644 index 0000000000..819d308f6e --- /dev/null +++ b/doc/histogram/limitations.rst @@ -0,0 +1,6 @@ +.. _limitations: + +Limitations +=========== + +*TODO* \ No newline at end of file diff --git a/doc/histogram/overview.rst b/doc/histogram/overview.rst new file mode 100644 index 0000000000..8b222659e8 --- /dev/null +++ b/doc/histogram/overview.rst @@ -0,0 +1,28 @@ +Overview +======== + +.. contents:: + :local: + :depth: 1 + +Description +----------- + +The histogram class is built on top of std::unordered_map to keep it compatible with other +STL algorithms. It can support any number of axes (known at compile time i.e. during class +instantiation). Suitable conversion routines from GIL image constructs to the histogram bin +key are shipped with the class itself. + + +Tutorials +--------- +The following flow is recommended: + #. :ref:`create_histogram` + #. :ref:`fill_it` + #. :ref:`sub_histogram` + #. :ref:`cumulative_histogram` + #. :ref:`stl_compatibility` + #. :ref:`extend_support` + #. :ref:`limitations` + +.. note:: To try out these tutorials you need to get a clone of the repository, since it is not yet released. diff --git a/doc/histogram/stl_compatibility.rst b/doc/histogram/stl_compatibility.rst new file mode 100644 index 0000000000..9b932a2cef --- /dev/null +++ b/doc/histogram/stl_compatibility.rst @@ -0,0 +1,6 @@ +.. _stl_compatibility: + +STL compatibility +================= + +*TODO* \ No newline at end of file diff --git a/doc/histogram/subhistogram.rst b/doc/histogram/subhistogram.rst new file mode 100644 index 0000000000..4e046a45a2 --- /dev/null +++ b/doc/histogram/subhistogram.rst @@ -0,0 +1,66 @@ +.. _sub_histogram: + +Making a sub-histogram +====================== + +Overview +-------- + +Sub-histogram is a subset of a histogram or one that is formed by masking out a +few axis of the parent histogram. + +GIL class histogram provides these functions as members and returns an instance of +the desired sub-class. + +#. Histogram over fewer axes + + **Task** - Get a 2D histogram from a 3D RGB histogram over red and green axes + + .. code-block:: cpp + + histogram h; + gil::rgb8_image_t img; + /* + Fill img ... + */ + fill_histogram(view(img), h, false); + auto sub_h = h.sub_histogram<0, 1>(); // sub_h is a 2D histogram + + Demo output: + + .. code-block:: cpp + + h is {{1, 2, 3} : 1, + {1, 4, 3} : 2, + {1, 2, 5} : 3} + sub_h would be {{1, 2} : 4, {1, 4} : 2} + +#. Histogram using a particular key range + + **Task** - Get a 2D histogram from a 3D RGB histogram for bins whose red color lie between 10 - 20 + and blue color lie between 2 - 10 + + .. code-block:: cpp + + histogram h; + gil::rgb8_image_t img; + /* + Fill img ... + */ + fill_histogram(view(img), h, false); + std::tuple low, high; + low = {10, 0, 2} // Since no check over blue channel pass any dummy value + high = {20, 0, 10} // Since no check over blue channel pass any dummy value + auto sub_h = h.sub_histogram<0, 2>(low, high); // In angle brackets pass the relevant dimensions in order + + Demo Output: + + .. code-block:: cpp + + h is {{11, 2, 3 } : 1, + {1 , 4, 11} : 2, + {1 , 2, 1 } : 3, + {11, 3, 3 } : 4} + sub_h would be {{11, 2, 3} : 1, {11, 3, 3} : 4} + + diff --git a/doc/histogram/utilities.rst b/doc/histogram/utilities.rst new file mode 100644 index 0000000000..5c4bf77554 --- /dev/null +++ b/doc/histogram/utilities.rst @@ -0,0 +1,6 @@ +.. _utilities: + +Utilities +========= + +*TODO* \ No newline at end of file diff --git a/doc/image_processing/contrast_enhancement/index.rst b/doc/image_processing/contrast_enhancement/index.rst new file mode 100644 index 0000000000..60c60cd468 --- /dev/null +++ b/doc/image_processing/contrast_enhancement/index.rst @@ -0,0 +1,11 @@ +Contrast Enhancement +==================== + +The GIL documentation sections listed below are dedicated to describe image +processing algorithms used for contrast enhancement. + +.. toctree:: + :maxdepth: 1 + :caption: Table of Contents + + overview \ No newline at end of file diff --git a/doc/image_processing/contrast_enhancement/overview.rst b/doc/image_processing/contrast_enhancement/overview.rst new file mode 100644 index 0000000000..4677055112 --- /dev/null +++ b/doc/image_processing/contrast_enhancement/overview.rst @@ -0,0 +1,15 @@ +Overview +======== + +Contrast Enhancement is a significant part of image processing algorithms. Too dark or too +light images don't look good to the human eyes. Hence while the primary focus of these +algorithms is to enhance visual beauty, some applications of this in medical research is also +prevalent. + +We have a few contrast enhancement algorithms in the GIL image processing suite as well. +These include : + #. :ref:`he` + #. :ref:`hm` + #. :ref:`ahe` + #. Linear and Non-Linear Contrast stretching + diff --git a/doc/image_processing/index.rst b/doc/image_processing/index.rst index 71c01c742f..c5bc61cfad 100644 --- a/doc/image_processing/index.rst +++ b/doc/image_processing/index.rst @@ -11,3 +11,5 @@ features, structures and algorithms, for image processing and analysis. overview basics affine-region-detectors + contrast_enhancement/index + diff --git a/doc/index.rst b/doc/index.rst index 2a52bd6fa1..71bdcf1200 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -26,6 +26,7 @@ Core Library Documentation design/index image_processing/index + histogram/index API Reference <./reference/index.html#://> Extensions Documentation @@ -37,6 +38,7 @@ Extensions Documentation io toolbox numeric + histogram/extension/index Examples -------- From 6e91e4bf5c1bc9b5def5ef60e3e11bc475cbf11d Mon Sep 17 00:00:00 2001 From: Debabrata Mandal <32168969+codejaeger@users.noreply.github.com> Date: Thu, 25 Mar 2021 01:50:12 +0530 Subject: [PATCH 054/193] Update image url links for docs for histogram (#586) --- .../contrast_enhancement/barbara.jpg | Bin 24897 -> 0 bytes .../contrast_enhancement/church.jpg | Bin 34771 -> 0 bytes .../contrast_enhancement/he_chart.png | Bin 14693 -> 0 bytes .../histogram_equalization.rst | 4 ++-- .../contrast_enhancement/histogram_matching.rst | 4 ++-- .../contrast_enhancement/matching.jpg | Bin 14950 -> 0 bytes .../contrast_enhancement/matching_out.jpg | Bin 7607 -> 0 bytes 7 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 doc/image_processing/contrast_enhancement/barbara.jpg delete mode 100644 doc/image_processing/contrast_enhancement/church.jpg delete mode 100644 doc/image_processing/contrast_enhancement/he_chart.png delete mode 100644 doc/image_processing/contrast_enhancement/matching.jpg delete mode 100644 doc/image_processing/contrast_enhancement/matching_out.jpg diff --git a/doc/image_processing/contrast_enhancement/barbara.jpg b/doc/image_processing/contrast_enhancement/barbara.jpg deleted file mode 100644 index 3dad19747e21279788ebcd70df7c3207482715eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24897 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 diff --git a/doc/image_processing/contrast_enhancement/church.jpg b/doc/image_processing/contrast_enhancement/church.jpg deleted file mode 100644 index c0beb7c651a51c87fa91d265cf94b41bd2cb72fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34771 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%0Dn3>Q_=du7oL8mhQphw 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 LKPoBc7eD{m?VOo` diff --git a/doc/image_processing/contrast_enhancement/he_chart.png b/doc/image_processing/contrast_enhancement/he_chart.png deleted file mode 100644 index 217c89ef1565ebdae0e03f2044aa931d954a35ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14693 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 diff --git a/doc/image_processing/contrast_enhancement/histogram_equalization.rst b/doc/image_processing/contrast_enhancement/histogram_equalization.rst index 0874c1c4be..2a52118c66 100644 --- a/doc/image_processing/contrast_enhancement/histogram_equalization.rst +++ b/doc/image_processing/contrast_enhancement/histogram_equalization.rst @@ -61,7 +61,7 @@ The algorithm is applied on a few standard images. One of the transformations in **Grayscale Image** -.. figure:: barbara.jpg +.. figure:: https://github.com/boost-gil/test-images/blob/master/jpeg/suite/barbara.jpg :width: 512px :align: center :height: 256px @@ -70,7 +70,7 @@ The algorithm is applied on a few standard images. One of the transformations in **RGB** -.. figure:: church.jpg +.. figure:: https://github.com/boost-gil/test-images/blob/master/jpeg/suite/church.jpg :width: 900px :align: center :height: 300px diff --git a/doc/image_processing/contrast_enhancement/histogram_matching.rst b/doc/image_processing/contrast_enhancement/histogram_matching.rst index be7883a0c9..0f89a9420c 100644 --- a/doc/image_processing/contrast_enhancement/histogram_matching.rst +++ b/doc/image_processing/contrast_enhancement/histogram_matching.rst @@ -47,7 +47,7 @@ The algorithm is applied on a few standard images. One of the transformations in **Original Image(left) & Reference Image(right)** -.. figure:: matching.jpg +.. figure:: https://github.com/boost-gil/test-images/blob/master/jpeg/suite/matching.jpg :width: 600px :align: center :height: 300px @@ -56,7 +56,7 @@ The algorithm is applied on a few standard images. One of the transformations in **Histogram matched Image** -.. figure:: matching_out.jpg +.. figure:: https://github.com/boost-gil/test-images/blob/master/jpeg/suite/matching_out.jpg :width: 300px :align: center :height: 300px diff --git a/doc/image_processing/contrast_enhancement/matching.jpg b/doc/image_processing/contrast_enhancement/matching.jpg deleted file mode 100644 index d03e07a0404975ff535f89ccfc1757869ae83e56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14950 zcmbVzcT`hf*JkL5R1He+EucsVQUfBrLqZ3U4hf2cPLwW+G4vifB%y|)bVYhqT0#>L zkzN#(qSAEuz4OlZ&G)VOV`gsF&dORj>#qBp{hYn`+2^@`Xa6n%SPgXzbO96;0070s z2k>_upao!{qob#zWuT|0zs$gJnVEx?nTd&+m;D+WhcLgWh!DS!pqP~6Ein)nBq$`K zDGOFsRaaLRmDV=UQqxz2sH^^S6AFgQmzkNEd01I_R3(HYRR5o^znuU!dWsvAwp0{C z07^CrDmIG0y#W4;=cJ+d*9G|ZML|hLO+!mZe~IDp#ehau03`(#6(uzl4GlH*#pv*h z{{hr&G*<*=HEFL}xX=k9fpXC)#q`2=>$}*Y6W>MTUHxM&F+{(>E}LSz1}!*xK2>sy?y-ygG0k4^5oR?%dYwH`ETiZYO4-Suh z{`!4<@((Tw0M-A5b@BZ_f&DjJY!|pFsi~=`>Hfh*K^bykRBY5Vg0i$%G%e^{kXMD| zqUnKmQ;O@mE(yy+zq7mgPcU2)0e=zw@ej0rA^X1r7W4mw>_340C$2dFBNfGk^QhPW z8h{^IiIOOW|8Af=B1??eM&?3rUh^R2g!J&{Fh=xd3bY{Q?Q{CLcQK8w?=_7mB5Te!v&(jEuT+Ge#YjkJ`Ks({ zB8T>b)Xygm}Ed00f$Q+tDzG{48-*+1(XCZ$=|^cPT|bx@`0A7Ip!=(P|r3PFpT zHfwb1jZd23`y+Ph&VBT2!A~jv0;UON(yJ;m;f&8otNh5RcWzbk3sq8CRp#Rpqtksq-*(bgI7l(=U@rsKf^4 zIUil1g%0I^oslAQ3LT)iObJ>7@L!Dh5uSyOb)BO{erl6A^C^Dy-JcQib!{v#Df)QY zzk*|TWXolH>bJ!(z@U>;J*L)S6O}Q_VBZ2eGuuy^ev=EiJvyqf;84Q9=T`V(Duy|f zwsq~r*QC3gj}AIoR$ab_zh@i^;E5Q%%=PB_(;GAA?gtuV2C0%ui&+odcTA2{R$@x| zo=JxK7+HK^{Jf`+&@9?|`4=D!Px6oKZ#0~QK2y@1>M(R7-5a^ampUi4D-Y8qJKT7% z(jUr0zTb1=5c$5GUsw2Kx-JP31!A}&v5|#T0e=2?9~uAW8o6-ycA#0TeKh zMAd7%-!2=AC!B#;mpLZ-`m}B?B2~0TnD%;0>PGT~Kh=gP;7Ptu-hE zCadn~Jkr;)Z+O5V5HRwtAr1+Xi!=G)N29GZC@1rBXJOocC!sm9`mv?H<<+5Jh(2c- zPTl8<=KlF#0J{1CLkktvTsDNEY*b_agT!BeH|hAs=Np`QDl;JR2OV0&2I;{?5zW=J?$hr4^{h0|2>-9ya2YM5i zs>laY>wucAK-oqB0Xm&Y!}kr<@Q3FJEnP^(0{z>!fo^&q=!AvmaJatfteu)a2Z(|L zMCE78v=XzWzjdx(W=kz?m#KqLDBPad1mWQe)&kB0GmCZ7#p{zCzA- zR5F0hHCZN zw=Q`|6_mE$tY2~Sd*8+G0uz|LeyM(9=Gmxvqu6^3GGhhRc6g*LT-#MY$k3jzPwm;w`ZrxTp+f%Om zArYi;giTS&@*6>!E6N#3r056vU5_ftWI9Edbu=2UGwCNZgq@ZZqO=|qJ;A4+BOKHA zG{^9&FQEdd#mD#k4#Avtkzt(oBc{Xx#wQ9STF$klqmuFhOb&ov(n^GC{g1A(X`T4} zv{$#*48IIN^~vsBDa3<^zx@&x{#0=a*rcImKaR8yxh!<$Rr6vRzhT1HhwBoa3GNah zAeTI@Iwd?Lj8N!m-}ahwP2y;*zWQcJkI*;!?GLUiRpNL40?tvJp;YM6na0SkxvZPH zYsue?4`GRfM#5ZHoBy)#CD@ZwKGg7FpCh}6l0c%)?Lv{H8wny9yVZu&G_`<=0ohdF*7iH#Q~{ZUvA|{VcZn$f)PrpII2Q44nae_ zs_vp=4ruIitW&HBR@K}pscbfL1-jS4YmQikz3|eKBb<5NKpf|GSW!hv{h4rWyMg#{ zHm`|_0{vT3%USQG=mE-H!ES7NGf#jqGD8`6iHXAs!0=Ex;v4m2bF26fQ8h9Oc9{tR zXHxrx6Gl_^z*S)Nn!FO1nfl@Med=kc-j^A>XwgVsCXOO&PE8;ojVKG2IU=M$*~ZDH zpyUZQ_NxpzZDbBBAujn_)n}a^m?u5uN7a>^i?1FLn-sCJYZ{>_8x!S2ufllz&lE$n z1aVZL$l^{BC0e4$#gIZA;EpxiRs)qB*CW?8lyv*2mA??#7wqy@`O~6Jhj-f z_$q%-GV#Sw#XQ?x5??qSQH8`Ce{b}%KfGr7T`b;(S|B=8jbhKFxmnVvc|_RX46h)s zFyUL7PvdaF&Mc3XmI7DD&jN>bcC4apT5sqECn{GN_r30ytO7JsofnTtC1?jUpv)4_!5H-AgKb6 z#EC6DCOclEX6Y|57~p+uuEbFk%*6soOox^)C3cd2+^H7m2N4H<0liWo-kp2KCQM5t zw}OVX&Yd~z0E7u_#5!qn?fasGLVGu@9#7K2B97+|@G2TNm=3e{DnOiAwpo2n{|f*= z{O~C0tww*jO$nl@4sp$dx|eE-y8`A&)AzRN^;>z6jaCd33RoFH3{ii~{`{w0+s70O z9rpUc|DfwkUE_Yq0mqYXCk%L(M;oJ;_=Wa;0>y`Y7+!6LxWMm!cFRDx+iN?~7*{zG z3($8epydMyE62x^!+Dk&$b@}?fg32x_!96&EfgWGHu4GCon)Uzp?hq$1Y+)C5&z%| z3>*>r71{C@h4s^IQO#Y`97j!dI#Xbv(yIbS&AXVBb*%P4T@?o*Wkf1Ho+_QgLnv0L z;0Uy1EZOmiKjXS&&t-8>pxUrQw_%aFfi>0T@YLtEo%s;CrB{~rIi7otLit19H4~@nB`H)Os2mHmRVF((L&Jwv%;k9FE9nrG*lkJ4+4k7cr%O-X z4_2dgp0=i}PL~bc`iJXPvJ4VAI@j0VZy0^v)FbaP=Bnlv|q1 z(A|;DdLEcUfXCOgV9g559Dnl1!6lc!bbMmpu&x-nCA?W@3CU1xpQRY1#Lb1n@ARoj z`{eC$p*2V4VZCp1Z}x+J=yC%X*$jdU9BuEvTVSzwciQb?(;4X!KeyC7P`KHfBc#-C z`(prsrt#DS-nh;D%B`Z9V5#BxntRQc^IT$IdJZFhvw1h&H?57R5Hah>e}iI+RP8Vq zp11ns>*uf}qG7;1yL4ZLbe!U;lRr@6tGoq&L)9f-MK@Ay-3m6o0OZ(Ub6r2w-;xye zBl1avf|O+4D%BCNhf%NE=h$4`Qdi`zA8(`z{a#)YLa#YK;5E-rLSr3I|A(@JqFliq ztRo*n;49{u#mw*LTChwW=M6}lEEv2BI)O4$9F{#4ujQL$$6e#1i?qKJhS#t+o$Pa_ zN;@WTIYNVR*H{z%RiVBs>J2N$lWr}C;NH%thz!P8)uChJb-ZxWpv{^^CWPj;L~!b; z|3$pF?2?k{o&J1dOpbA&HAiewI9u@6D%%=&`p z*Yiz=Aau*#9M5vmVb(|d8H(GoSb(q6mT~C8;L>ED2^by1M*qZbg|=`WyiCC%tZ=Q9 zg%il!6F;H|EU-NS$8m&DpKZlMK?KY@^-V&d%umW}TbOZ(xia{T$Gwn=e-W#kMw6@raXG(a1RwYW#W^^8x0%QgmE(esP}pWd+S*s^c_VSnXd zBIxGPI~p>yGiFArC#u`>wx~Ffh>P!GUfZB3PtTR#I;O(n1t=KcqP%alKQJqPd7UqR z+l6faNVAawEqg%XMlnBMy*Gd@4GT=%)e4z~JV&*L_%}`XQ7M11nXb@s|BSdVKjO@P5>gChYy{RZ-z80GQwAErhj`Df{!8MGVj>aS9Yh(9o)}e< z8y3l|67}i#(s+l=@F77g{v#D7hdWnolTkoAH~EM?fHoInffSNhHBjH$7(XhzItohb z;e^in=^>3}K!9rxl2CLmU(3ktVv-uKqbC;liHg5|B+J#X5M^z1)qa?;bHWe0B~GxO z^O;Mph!n2SE?wIw$?CM-hjlRnGwk9YUw^1^JP9s~yL8cpL3n4zL+SYC^+Wht%D%dg ztk2+U?I0Gl3zr%tcixsg-?Mi#zatP!DqS>U_bu%p%Gs_M(r_$iadHy0bUoY4E;dkG zHE8)8k!q0^^FL~6+qK;T99iNYMD$eY16`FPLkp8Ej%R%NBHe$yW^k~)FE5Rjs3{$0 zm?aC&KWa&IS4ge2i%G;Sx9PiH<|^8xmEsy59&oy*n3kf@ItV!8v8rpWOgVcMgW_ZI z)|Hb=Dafd1V@YQy^wp6CaQ+1U6agtPWx0Zw*VzPDk1A3X=7gPMD~# z9Kad}vL+5`ylVLrW*bsLkLd$m8~a2(aqt%q2vyL(je%Zmc=9=4`aUm>7|CH>!;qmF zS-QS@Smp-ChfDB1Cj!KjaPuiKpx?M68_v)0o%zM&M9?bjnJSsycSU$Eio*;K#;`%X z^4tyxuL|vH!9?&6Q!3#!P0DPxA87`K2j~c8FbNd*Y(oeOU-iswUd*}4mZA^ug_+24 zko8fpBzw2O0iwkXZ#r+B8hNV>IOzV!LT1cY(pA>j#S*ichzdQc_y}QP=e<8A-1|b{ zk6^5&RFm}fMgmmrwRcB}9j}&645LSgZ)DNxOzCU--7C&Awfl9v3I2<;4YWqNO}u~v zvzfGWsj33CQk3HTS|ZX_G;F?p*JJV+e!sPpA7t^$)g96b98tgXJ<4pf%!i>_^1T07 zjaTtpMt+>BjtE{5Ci3*hmMI+-72Z0>q_@S@7KtwU%;Ji zbQaLx`t(&p3 zlko%D-((7$@3zt*q5K%R)u*rNQb0$>VxBSU?!G<5$p}+{2ZSG4>_8vznu#xBm)Mmk zqcN_k0f&U$0Y`LlbkZY)u?2G0g-~FkRCL0RxSJQEU?WdSd5NP_|pUW=+Z3FV&nsUTJ%c6na@c3zeM)3~D2 zpRJ2*wA@Tq>e^PB49R>S1i$$vFoj{dVeP%hDC+QdtEIq0N2)?kqKC$miG8b3+LpX> zaO>(s1Y(mFWv!c-ld5+JupVA+mC^`3{9fOv`il?9$AW5n;NP;VGAUaVS|!`-e@Wyq zdgH;a;aWUdwZhd*&Uj_bUMP;IZb&1+w((x{_>q&(?ctBI6I$tGL{JhP`wh(S#%7`*2<(=ic!t$c!@x|;b2tguFan!pyqk$)RXAv1I%oV~vHSy*D2RY{m*pz; zRnHJ4aJj!qEals=hD@&4??eQBrmC&Pb^_`FL25Qu;>ByrrF&v1{(O>wk(Bhu~~ zNoHjBx&;hdSNTmh*10}<4tjJ)a>c7lJ5GUXVYkECCHyt@>L%5#x`&mlzqBVH9*W8y zDwqgJD{<15L$>D}gtVTg8mA?cHF}8!N|aUH96nZJkQ?ichG}{==x~Rxi{>ttVkexv zYlKBA>prDnNg|h9kYu>RrPyG5#?*PlMx#xN>`=dy?jc-lzal4~m~jtn+ZmS9$WE049qRd5SGN-=A?>XJpT?_z1 zqtK%`OuvFP*W^4Rc=q6x!pX)X%KCf8DFeJHE{ZW22I>aBLcz&5IiAxQ8nfWw6GC6Q z=DDcB=S^hc=xh3ODeCmc%D%=qN@|j@M?IF=tCv}wm2+R$(culM5l%EG-MMBMZaW>b z+$yONR)D8Lc-J$*9m?miqc7P@A9OOhGj!{YxsnD_S&(M7nMOsie`rkXQj|_F8bi|$ zsssYgbc@-7(>x{^$~!sAKVI4)Rm1c-`aJzX@`5Y9A7s&RC}caVHK$Xl+%|7Ycp`TE zk!cCG7nW!u6N~mJeoFE}L+J^85)vk#UhA6xyOG9jujuUEAhOs}ul4+=ChwyD965Zz zv*+!GYR6<(<(}pOQI9NWHWHjoqnuK8PB_^*3#AplAD4r~1yGjmX$6vL5S_ZXE*^{}^YIBo^0V?BGYxwW!PYKLaVE{; z+Qy_gliYR+UKAKc|5bzmqlX|c5vdNfzFk8R~XT_EKT6XV(QCykI z-u2d=QBTNGE5}wrs1(~3T3o~P!nSaveh*J#$7o&{ZAu%LPKo?XK?6B4CU3Imo3We0 zDvUW`%j}52*D&P9O}ib*zlEL&PA%gPwm3^* z-!%WtQZOK8?dh9NH!Hl-`X{KUu@ILxE3QTvneSZ#sDYmu@0NbQbyvT0Ou?Urs^LdL zqY1>sJ}KHx{&ALFCjAig!MNp5k<0@Ty2Olvv3Itm5S3|+7Y}-569nfD^31~0FPDbh zE*8N?DN!j`?n#ZVGRCoAQ(iGMcMZr}FTJ^A77&e&A5R>7@dg_V&u0QmU3c~vDz^=Q zt(UrN-5F$3sDw(2@sN23or(~zx{Cu9^y%T92b!=gQ%f*V{~6ve;X1nnFq?%J4yU;( zz)EIPIwD|c!DJI9GLx(<;Ua#MO`wAH&$8j`*$B)m*~Ayrj}5z+M?yBdA3OPBw#Vww zoV(92mi#z!Q>>bcSW?d?8I`!tcEUBS!Pw3WKl3}%E2@lA>cn?FDkIX`{f(Dd|Yy=>ZoJ_VUri(3Oi0J3Q&azuh zM^-~sBwZ6L??jXpr6Ap683yYdpJpxmQmKfvx@pj7TDF+|5?VP~r(GFgf7cr*yNoMN zK)ZlfOuz%kGH}lTB5ccqVB|)BgxkEB;(;Fu+PA?4hdpNM>D`hkdC!vRyMW_n!ik`d z!cA~oK+4Q?`AFSr9@fx;T)-rze8J(tA9^bCkuy4f8emVLNL;wP#o1=fHb2GEO2^B6 zxdt!Vu}7s?vKgv>f`2Uo(nX9@Ual{1Uk@@5A-qUSEBI6yej290a9DP0M7)mur3&l* zR}O=%xdx$hc4{l3;^ZVmcy*!JXH!Z0o7*qvyS7ZVQnswRHg=%Ql3XHz&(&Zmq-z2v zWTV@mowx_dE72y5EGVT>gCRWKTuiLro{1-{Q;qztfh0kpYU$g)ZD@K9kB=hC)F8S5 zKTJN}*|hXYA_S!3PJ^E{RJU=VP^L3|AgW`nJLzP#bV5q@NMvPaF$F`?`;xap*Md; zpHoUxsbbNHOOGJ>izu&EY=mwzp(dx#$vWN4meV(16`NKxR~Skba^B<#6jrWqG?CF%$G?h@?B}#$4tc}8D!qc0UE1SudKX+;VO^y+i1LUd+JO5 z$)wU#TawJcA|_-+W^i^jA{>N!<2Nc34&pB6oicMYzZbe8lul!@pF}F~pIxH3)6eNf zl}WW@;V$n>w*{^HhQ`FO5ak{yJo|#N3ckyYxAT3T=b9#fzBG#)vS_f|U=QqP`M?Y! zvK2`~StrY5IV#RxC(>NvR7-po#c`dZ%nff;$B0^gMkN3R$+mP7>S+tfwFu0<_f1(0 za=>>ozYUh>2QND&^Y%BHC{h6rItf_s%)!}%==pZV=)*F<0AqpW@{xKb@F_XT(4>{1 z-!t@+u#6Lc(O<+2`fob7?2WC2IHQ+0`ksQ$w*rn?{yS^{(g~Tc zj!}QiwP%;3Vwo%b6_bv)jIx3<}e2Uh6z{6}JCjXuX3y>d$Y;h#nGmoQS`kb2* z9Q;IZvcq@0fSVHGD_?pi6ZmihWU+TlHblE}AK}0_zTQ7zl9?Y1sXX(dc2LSYg|Zo6 z{4%+yk4taMkeMI}+57P7te~%*l;5^a3?i+ z|9s_zb8%Qm)W9QR2c}yeQQbSn22KxD%lY!_UhGcLTlRP2ZGY@I1?jxMofOHUjbdX!9Ljf6Q(kKpEt zP?2`%qbCjiamV+yG=twgi_x}98DS~5@GBjXXLJx&GPp~?+&|y7`)NpuIVi>T@WfTZ~0GraG8rStJ zT1`wxQ&=40B=GeO&H)B%;>G+4vS?jXNjbUTq+Zgo^U2DNzAM1b@SKTq{4N~=z8oXs zv~;c`=&w?7rd%o3%?fJ8bGRjI;v!8CCvH3x1kwmV6Cv9vY212-PO4X0`nI+fPv7-S&oDTk zPd{x}Omj0p^B3Wj^R6ii(&T_6Nb5Hg?4*?-=uD#+_U8-Xb>S}da@}6rMNh;4_h}o4 zz2r9TwsVJ>hxy{nYVDm9T=sky?k~Z5&24}(@p1DKh+fYHvCzF}LCjG2>TE^lt z=#JuXbK=^N4n5kIj^y`?RGWAJ9i1+ho>CChDSfQkXgE1y0zUit(md_D^g; zV|j0qW?Ig6lCE+q2!DJpd0N9#94^Bw>8^(1s$qdt>L@{WFh>RjH&^`Y7nNyq)IFQ+ z(q0HDtCti!uLsYZzkft0xuAq|yzlSxcru%*2dh;lvf<376VY1pg{>l79>8JJ&F+o#MBXGW(1D2K+O!MUBisVd4gM zWEG+6KqZvjoE94~sJ!?53Y|Ut{cSqU5y71LBv7Tm*MR*d`BBwzs~4Q7%qH@wVXGHC zQN>hs{iI1Ur~9L~v;1GjrC7q)fiYo+yzm0|r(Vu{tK*euCBS2n>`>?4%$9LJ_4y8C zrqx&xo`D5w`xnq?7z!-CIR1csz zh{1Y#0c7*nh&ZfpIEo9!02d(-b*?U#F0}3#-<2hJK{Qm5$L(?AW_UT<0V@G_1~&no zr1VqErzonxr}W@kZ09RW3+q((?ti_QSDWX^q9bR^%$Kto}vC^ zl*{NK|J;0g@F;bHEBg2>;qr5Y zQ3w+URr!v^^TYDVyz|ZDUNC1oE4i9blUo0ru+=SjXF^d~SK?e@gnUs|n2J ztzbli^$Au{^n6jB~-A>BM5*;jnj$f{Rd0Hw=~{0-uXloK;ND zx%i1;CJc1YF4FE~-5{@2=`aXS=yX!>G`a~ZoXzmRm-d2T(Fl~$1&6>8pYYT6J#Kqb` zdJcI?6(9X;O1**RQbh+bZGQnYi2FC%0uu($SMARAj%x0fy>3hG-Nzk5#^0x6z671+8Rsbv-^cv~!OQ_Am^P8axu*EebmmrwmswQMNi=Jt%Wk zbQL&zdk$wW=3dP&+K)7ZJ2EmncA!y$+H4ui%B*~NG>U5&Cm|OsA4(qb6l$98+f;BT zRS=SaXQh0TJ(^Ejc`47saOQfdt$Wxoo{R3ERKlnVDOTT%$D9eED)7S~56Yl5GZ)2s z0=pz<6gTjG@^pN!-YCN9a5K%-tzz$}$onoa@%~|{=i}xLGvR2gvAI+{ka=MuYI1jS z;h+;Ig%JjlNqOO0^vl`tgX_r{E@(h9Y8+*CrC+HOxDSE1d^5Q+VZXmDrK;@f-hYvz z3cBAe!wbR|;mmfk_3GHYj9eL1J>q-T`gN*`yY}__XBxsmKYvwkYvu4Nq28vx{+Q3D zU`(`}_&sKybH5!4Mh#^NE1w~hP5FiMpu+r)uq^77IoyQmRT?OjV3&G^Kt|Sc{`kt_ z{%c*QWyOb8fLQ$yak_Tu<*I~1|9l8&*~+EhX%KC)ZEgsVJV@{rIk8CF(WlunMmXjL z6AJP!3ih8x6IjDw-rk}-iv?0pQ3&HWlOXUpng(gRW^4n4@ttLeTF{N(&2r;;!G{*1Uf8n~6an!YadSg~y7 zUiabh+c?`&TBsIPvL52_KJ$)Im7UArby-GzNaWm^PwZ90v3$ODhZk)>p`VMJXm#P{ z2Z<#maQY?YaFAQ@X%P!*6%u}FgX(MFi=9q(>k;$UJpvo$V|bHWC+)qf7?@}r?!KJH z(M$5;(@P((SsKxuG%Xf>db9=8Q5n|fk=ZUIS~s^`DITRtEWriZ0r}f{N|_seE|~nf zx$1^RG^-s|Zu$Rk@!|<&rsGe5;CWe}3AYQ{rPgm?6Z*iu!kK(;8FX*vt(pVg@BanZ zN4Nc$!;8M?*DTKk9E@D!?f*Ff{tl_=-Dsx7ev;LR%{J>!J7%z6PI#Bjj+B!(DgeC+ z-(XTYl0i-;_ooYCQWvN!mpZ<(C~{$_S8(*|puse5DB z!1eJ(__o14S^V;E@#jBUI4UF4xBUXie*tY~`$TtNpR5rd_^~e%ahhs>!@YaHF=n$7xTJ zg{Ge_#b~%h-3xhrTjs+LXsF4Es5hs?LH-V*Gg5yZ?(F2j17T5o&t1MG)CI)v7DO?`Z zUyj{2MR0D|n!m}TIGMtEfJABrcfDBq?~R(d?AX52Cn*OjixsNvA;s3WW>J<_GxYwY zLqwZ7$nA>*?*RET-6`&h4dLG^ar48rJ^iZ9LZBHoYl-b-d7zmAKez47e6SG8fPAw zEM*xk%p+waTFvbgqONZ8;pV#>D;NnSf+;V%gPZ+OTs7lE6rX%o6F1H5ZEM*^XU^vn zi^$E+hc|8xzj^p5<+MUU@PVI>wF^%yHHfca!HDC0evj(6!LW0jl*|)| zQ%w>_$LJpL=(12{mKllljw-8BY{TotCekMNy`?^62lg?}>t1x+z;)6T5M^*wMyR=l z$|c-l%Qomuy9CFd22wyD73Acxd9TE36vUfCHa7OR`D51-`D+6ioX(N)-$9(BX?yAu zn+;zn8{TuX54k>Oz#Q*i38>@iAsWZmVqJ0jRay}06U6o?p>FL|>(y321D zlGblB{gYjeGT+i53S^Z?jvAHefkPYrDAsRrU=kLRq2#MqPPpE3A%rT3hcjYOg1Aow zhrTOB%9e;9&&@jw0YRIZz}z}`h`H*^b&X#d_F94(ouRm!1vH#}6v!tFk(Y3Ce*v#1 zeQ}@eIKl+^JK>k*gO(mcua&=lcw*t|t052w>__-Opl$E;{i_Z%%J7b^^W(!oK&y`m zBg1#zWFKupe7M3Ezx+}@VKID=s#aq+YCy2D6etPHs8YcO{Sy(9Y`^D-1veMKxOsXh*_y;(mg z9k>$>X^7))`Pws%-feiq=71;`Rzj}^VG6j<$%}8d8+a5P__dz)_cyi-`6FxAorl4Cpn&7-$9Fp|EN|sw$TU#s+?$RH@ zrc-L^Psf!`%scxp>yX>HG-Z6%cOnC)E`Nae|NaXw|C}#B(&84)YI=-_*t6#rFWxd- z(p&v7@^<8aIS*;Z;`f$gs=cdEawr88_|0$>=$>b8)t~!g9s;zB~olGnpXGqpn+91UY_McyPOkURXX_JaL&-wFi zaSE>j%^hFsb#>A4>2Uqov5a}vK>iFVa5>95^GU~*v~y@`+VQK}zuhtV*AmJ8mk9WO zUnc+f{lYG}w4~AwEAkGw**DES^^d}RjnuMjury@Qo!l`jnCF#1g(5b-v}1V~ckgT2 zvu?8)M49#|9VlPY4Dhkg0Z4zuN|_28y!Z`WkQ>|H%h~oZlQaVxWV9mNq@_doEuEOv0%t;2 z`Fl(M1wGc|1s2~!lIW0Vu-V&5=xDw4y(sp=+c+~eX~+mP1kN2$@3!q@OA0qt$u1`f zS0Gl*!{q9pRP8KG$$6Kw0im;B^41iCFXV)B|3Lkh{TEU6Kl34359KtV4is(_1yYdV zd^a;G>$bV^7(Y?|(C}~{V$<8^_U9u_IihA(5cSf=Fb|W5L6E!>hcJ6Z-pLsSV>OS; z@x!{~1jC50NB3WB^O{KU*oc<>9*nv2hvzSVtp68Ke(}!4=C>ejhr^ME9f3o-l;1%$ zAX1*9-fO+9hOd4(mDa@X{|cPb1b*)?Zc|SB0sOu&d8i=zsgv;9-STk-6$*QUI=6i* zRU$cHG7uaWy#6Xm);KLnF9e!R*Ss-I}0B z0rI2Dt;re1^4~G17Q^5IOT9mK1D$7PsP{E(p%Jgtu*VsPxOX0J&JA~K&2%e#d9wp zEz3uY(2udYxRuumWQ*vw)D8>SjUOGKQp{Ft11(jIir*sr9ZDmEqyub)^qdlVHsRgt z$O{G0Hss~5cfC)gVu%H{_w?St*%3m0#xHX1bwuFfYc|s>vvVVYe(n1FFk8L!s{mXCDtKLqak{>txl9)uabLi`}z%2+H}e@)5w>{Gfe086_I1B9r+54_cTuy zP5uJHu@~dkBH^0hnIVXeob14vpLI!}9kmPk;{0wQ!G#fC4^HY8c%eNZp8g*`e|+)L zWci*tvIu_J9i9`>v{+)ZKGQ~VIw-FhNrJ07F#l~}9Zba>+v;(AD#;?=hW ey+4kv5E4YFky}EucyRasfh>TMi-P&@$NvFKSNG)r diff --git a/doc/image_processing/contrast_enhancement/matching_out.jpg b/doc/image_processing/contrast_enhancement/matching_out.jpg deleted file mode 100644 index 869043accf89bd69a4a07fd75c81be676121295f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7607 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 x#J|?b-aYJtUE!DC$9t6wf0rp-PDf7a-bm&q< Date: Mon, 29 Mar 2021 00:30:12 +0530 Subject: [PATCH 055/193] Add rasterizer support for ellipse (#585) * Added all standard morphological transformations * Should handle grayscale dilation/erosion * Added test cases and improved code structure * Should handle multichannel images --- example/Jamfile | 1 + example/rasterizer_ellipse.cpp | 44 +++++ include/boost/gil.hpp | 1 + include/boost/gil/rasterization/ellipse.hpp | 192 ++++++++++++++++++++ test/core/rasterization/CMakeLists.txt | 3 +- test/core/rasterization/Jamfile | 1 + test/core/rasterization/ellipse.cpp | 83 +++++++++ 7 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 example/rasterizer_ellipse.cpp create mode 100644 include/boost/gil/rasterization/ellipse.hpp create mode 100644 test/core/rasterization/ellipse.cpp diff --git a/example/Jamfile b/example/Jamfile index df17d57c42..3ce548cff5 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -32,6 +32,7 @@ local sources = mandelbrot.cpp morphology.cpp packed_pixel.cpp + rasterizer_ellipse.cpp resize.cpp sobel_scharr.cpp threshold.cpp diff --git a/example/rasterizer_ellipse.cpp b/example/rasterizer_ellipse.cpp new file mode 100644 index 0000000000..461103ac9e --- /dev/null +++ b/example/rasterizer_ellipse.cpp @@ -0,0 +1,44 @@ +// +// Copyright 2021 Prathamesh Tagore +// +// 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 + +namespace gil = boost::gil; + +int main() +{ + // Syntax for usage :- + // auto rasterizer = gil::midpoint_elliptical_rasterizer{}; + // rasterizer(img_view, colour, center, semi-axes_length); + // Where + // img_view : gil view of the image on which ellipse is to be drawn. + // colour : Vector containing channel intensity values for img_view. Number of colours + // provided must be equal to the number of channels present in img_view. + // center : Array containing positive integer x co-ordinate and y co-ordinate of the center + // respectively. + // semi-axes_length : Array containing positive integer lengths of horizontal semi-axis + // and vertical semi-axis respectively. + + gil::gray8_image_t gray_buffer_image(256, 256); + auto gray_elliptical_rasterizer = gil::midpoint_elliptical_rasterizer{}; + gray_elliptical_rasterizer(view(gray_buffer_image), {128}, {128, 128}, {100, 50}); + + gil::rgb8_image_t rgb_buffer_image(256, 256); + auto rgb_elliptical_rasterizer = gil::midpoint_elliptical_rasterizer{}; + rgb_elliptical_rasterizer(view(rgb_buffer_image), {0, 0, 255}, {128, 128}, {50, 100}); + + gil::rgb8_image_t rgb_buffer_image_out_of_bound(256, 256); + auto rgb_elliptical_rasterizer_out_of_bound = gil::midpoint_elliptical_rasterizer{}; + rgb_elliptical_rasterizer_out_of_bound(view(rgb_buffer_image_out_of_bound), {255, 0, 0}, + {100, 100}, {160, 160}); + + gil::write_view("rasterized_ellipse_gray.jpg", view(gray_buffer_image), gil::jpeg_tag{}); + gil::write_view("rasterized_ellipse_rgb.jpg", view(rgb_buffer_image), gil::jpeg_tag{}); + gil::write_view("rasterized_ellipse_rgb_out_of_bound.jpg", view(rgb_buffer_image_out_of_bound), + gil::jpeg_tag{}); +} diff --git a/include/boost/gil.hpp b/include/boost/gil.hpp index b98af56e6a..0df6b59fb5 100644 --- a/include/boost/gil.hpp +++ b/include/boost/gil.hpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/gil/rasterization/ellipse.hpp b/include/boost/gil/rasterization/ellipse.hpp new file mode 100644 index 0000000000..43e51e8147 --- /dev/null +++ b/include/boost/gil/rasterization/ellipse.hpp @@ -0,0 +1,192 @@ +// +// Copyright 2021 Prathamesh Tagore +// +// 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_RASTERIZATION_ELLIPSE_HPP +#define BOOST_GIL_RASTERIZATION_ELLIPSE_HPP + +#include +#include +#include + +namespace boost { namespace gil { + +/// \defgroup EllipseRasterization +/// \ingroup Rasterization +/// \brief Ellipse rasterization algorithms. + +/// \ingroup EllipseRasterization +/// \brief Performs ellipse rasterization using midpoint algorithm. Initially, program considers +/// origin as center of ellipse and obtains first quadrant trajectory points. After that, +/// it shifts origin to provided co-ordinates of center and then draws the curve. +struct midpoint_elliptical_rasterizer +{ + /// \brief Returns a vector containing co-ordinates of first quadrant points which lie on + /// rasterizer trajectory of the ellipse. + /// \param semi_axes - Array containing half of lengths of horizontal and vertical axis + /// respectively. + auto obtain_trajectory(std::array const semi_axes) + -> std::vector> + { + // Citation : J. Van Aken, "An Efficient Ellipse-Drawing Algorithm" in IEEE Computer + // Graphics and Applications, vol. 4, no. 09, pp. 24-35, 1984. + // doi: 10.1109/MCG.1984.275994 + // keywords: {null} + // url: https://doi.ieeecomputersociety.org/10.1109/MCG.1984.275994 + std::vector> trajectory_points; + std::ptrdiff_t x = semi_axes[0], y = 0; + + // Variables declared on following lines are temporary variables used for improving + // performance since they help in converting all multiplicative operations inside the while + // loop into additive/subtractive operations. + long long int const t1 = semi_axes[0] * semi_axes[0]; + long long int const t4 = semi_axes[1] * semi_axes[1]; + long long int t2, t3, t5, t6, t8, t9; + t2 = 2 * t1, t3 = 2 * t2; + t5 = 2 * t4, t6 = 2 * t5; + long long int const t7 = semi_axes[0] * t5; + t8 = 2 * t7, t9 = 0; + + // Following variables serve as decision parameters and help in choosing the right point + // to be included in rasterizer trajectory. + long long int d1, d2; + d1 = t2 - t7 + t4 / 2, d2 = t1 / 2 - t8 + t5; + + while (d2 < 0) + { + trajectory_points.push_back({x, y}); + y += 1; + t9 += t3; + if (d1 < 0) + { + d1 += t9 + t2; + d2 += t9; + } + else + { + x -= 1; + t8 -= t6; + d1 += t9 + t2 - t8; + d2 += t5 + t9 - t8; + } + } + while (x >= 0) + { + trajectory_points.push_back({x, y}); + x -= 1; + t8 -= t6; + if (d2 < 0) + { + y += 1; + t9 += t3; + d2 += t5 + t9 - t8; + } + else + { + d2 += t5 - t8; + } + } + return trajectory_points; + } + + /// \brief Fills pixels returned by function 'obtain_trajectory' as well as pixels + /// obtained from their reflection along major axis, minor axis and line passing through + /// center with slope -1 using colours provided by user. + /// \param view - Gil view of image on which the elliptical curve is to be drawn. + /// \param colour - Constant vector specifying colour intensity values for all channels present + /// in 'view'. + /// \param center - Constant array specifying co-ordinates of center of ellipse to be drawn. + /// \param trajectory_points - Constant vector specifying pixel co-ordinates of points lying + /// on rasterizer trajectory. + /// \tparam View - Type of input image view. + template + void draw_curve(View view, std::vector const colour, + std::array const center, + std::vector> const trajectory_points) + { + for (int i = 0, colour_index = 0; i < static_cast(view.num_channels()); + ++i, ++colour_index) + { + for (std::array point : trajectory_points) + { + std::array co_ords = {center[0] + point[0], + center[0] - point[0], center[1] + point[1], center[1] - point[1] + }; + bool validity[4] = {0}; + if (co_ords[0] < view.width()) + { + validity[0] = 1; + } + if (co_ords[1] >= 0 && co_ords[1] < view.width()) + { + validity[1] = 1; + } + if (co_ords[2] < view.height()) + { + validity[2] = 1; + } + if (co_ords[3] >= 0 && co_ords[3] < view.height()) + { + validity[3] = 1; + } + if (validity[0] && validity[2]) + { + nth_channel_view(view, i)(co_ords[0], co_ords[2])[0] = colour[colour_index]; + } + if (validity[1] && validity[2]) + { + nth_channel_view(view, i)(co_ords[1], co_ords[2])[0] = colour[colour_index]; + } + if (validity[1] && validity[3]) + { + nth_channel_view(view, i)(co_ords[1], co_ords[3])[0] = colour[colour_index]; + } + if (validity[0] && validity[3]) + { + nth_channel_view(view, i)(co_ords[0], co_ords[3])[0] = colour[colour_index]; + } + } + } + } + + /// \brief Calls the function 'obtain_trajectory' and then passes obtained trajectory points + /// in the function 'draw_curve' for drawing the desired ellipse. + /// \param view - Gil view of image on which the elliptical curve is to be drawn. + /// \param colour - Constant vector specifying colour intensity values for all channels present + /// in 'view'. + /// \param center - Array containing positive integer x co-ordinate and y co-ordinate of the + /// center respectively. + /// \param semi_axes - Array containing positive integer lengths of horizontal semi-axis + /// and vertical semi-axis respectively. + /// \tparam View - Type of input image view. + template + void operator()(View view, std::vector const colour, + std::array center, std::array const semi_axes) + { + --center[0], --center[1]; // For converting center co-ordinate values to zero based indexing. + if (colour.size() != view.num_channels()) + { + throw std::length_error("Number of channels in given image is not equal to the " + "number of colours provided."); + } + if (center[0] + semi_axes[0] >= view.width() || center[1] + semi_axes[1] >= view.height() + || static_cast(center[0] - semi_axes[0]) < 0 + || static_cast(center[0] - semi_axes[0]) >= view.width() + || static_cast(center[1] - semi_axes[1]) < 0 + || static_cast(center[1] - semi_axes[1]) >= view.height()) + { + std::cout << "Image can't contain whole curve.\n" + "However, it will contain those parts of curve which can fit inside it.\n" + "Note : Image width = " << view.width() << " and Image height = " << + view.height() << "\n"; + } + std::vector> trajectory_points = + obtain_trajectory(semi_axes); + draw_curve(view, colour, center, trajectory_points); + } +}; // midpoint elliptical rasterizer +}} // namespace boost::gil +#endif diff --git a/test/core/rasterization/CMakeLists.txt b/test/core/rasterization/CMakeLists.txt index 2ab6dd2b43..522e743ddf 100644 --- a/test/core/rasterization/CMakeLists.txt +++ b/test/core/rasterization/CMakeLists.txt @@ -8,7 +8,8 @@ # foreach(_name line - circle) + circle + ellipse) set(_test t_core_rasterization_${_name}) set(_target test_core_rasterization_${_name}) diff --git a/test/core/rasterization/Jamfile b/test/core/rasterization/Jamfile index 750eb46697..6be4cd9705 100644 --- a/test/core/rasterization/Jamfile +++ b/test/core/rasterization/Jamfile @@ -10,3 +10,4 @@ import testing ; run line.cpp ; run circle.cpp ; +run ellipse.cpp ; diff --git a/test/core/rasterization/ellipse.cpp b/test/core/rasterization/ellipse.cpp new file mode 100644 index 0000000000..1b9e813136 --- /dev/null +++ b/test/core/rasterization/ellipse.cpp @@ -0,0 +1,83 @@ +// +// Copyright 2021 Prathamesh Tagore +// +// 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; + +// This function utilizes the fact that sum of distances of a point on an ellipse from its foci +// is equal to the length of major axis of the ellipse. +// Parameters b and a represent half of lengths of vertical and horizontal axis respectively. +void test_rasterizer_follows_equation( + std::vector> trajectory_points, float a, float b) +{ + float focus_x, focus_y; + if (a > b) // For horizontal ellipse + { + focus_x = a * std::sqrt(1 - b * b / (a * a)); + focus_y = 0; + } + else // For vertical ellipse + { + focus_x = 0; + focus_y = b * std::sqrt(1 - a * a / (b * b)); + } + + for (auto trajectory_point : trajectory_points) + { + // To suppress conversion warnings from compiler + std::array point { + static_cast(trajectory_point[0]), static_cast(trajectory_point[1])}; + + double dist_sum = std::sqrt(std::pow(focus_x - point[0], 2) + + std::pow(focus_y - point[1], 2)) + std::sqrt(std::pow( - focus_x - point[0], 2) + + std::pow( - focus_y - point[1], 2)); + if (a > b) + { + BOOST_TEST(std::abs(dist_sum - 2 * a) < 1); + } + else + { + BOOST_TEST(std::abs(dist_sum - 2 * b) < 1); + } + } +} + +// This function verifies that the difference between x co-ordinates and y co-ordinates for two +// successive trajectory points is less than or equal to 1. This ensures that the curve is connected. +void test_connectivity(std::vector> points) +{ + for (std::size_t i = 1; i < points.size(); ++i) + { + std::ptrdiff_t diff_x = points[i][0] - points[i - 1][0]; + std::ptrdiff_t diff_y = points[i][1] - points[i - 1][1]; + BOOST_TEST_LE(diff_x, 1); + BOOST_TEST_LE(diff_y, 1); + } +} + +// We verify all test cases for the portion of ellipse in first quadrant, since all other portions +// can be constructed with simple reflection, they tend to be correct if first quadrant is verified. +int main() +{ + for (float a = 1; a < 101; ++a) + { + for (float b = 1; b < 101; ++b) + { + auto rasterizer = gil::midpoint_elliptical_rasterizer{}; + std::vector> points = rasterizer.obtain_trajectory( + {static_cast(a), static_cast(b)}); + test_rasterizer_follows_equation(points, a, b); + test_connectivity(points); + } + } + return boost::report_errors(); +} From cea6ef2752cbfc4c8d1991d1d96b9fc3f78d79f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Fri, 2 Apr 2021 15:53:03 +0200 Subject: [PATCH 056/193] ci: Fix availability of toolsets in updated Ubuntu images of GitHub Actions (#589) The `compiler` property should trigger creation of `user-config.jam` with the expected GCC 8 defined. Something has changed on the GitHub Actions images or Boost.Build and GCC 8 build job started failing: /home/runner/work/gil/boost-root/tools/build/src/tools/gcc.jam:203: in gcc.init from module gcc error: toolset gcc initialization: error: version '8' requested but 'g++-8' not found and version '7.5.0' of default 'g++' does not match e.g. https://github.com/boostorg/gil/pull/562/checks?check_run_id=2246393722 The compilers should be `install`-ed explicitly to avoid such issues in future. --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c3f063b6a..0d77b4dc6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,11 @@ jobs: - toolset: gcc-7 cxxstd: "11,14,17" os: ubuntu-18.04 - - toolset: gcc-8 + - toolset: gcc + compiler: g++-8 cxxstd: "11,14,17,2a" os: ubuntu-18.04 + install: g++-8 - toolset: gcc-9 cxxstd: "11,14,17,2a" os: ubuntu-18.04 @@ -76,6 +78,7 @@ jobs: compiler: clang++-6.0 cxxstd: "11,14,17" os: ubuntu-18.04 + install: clang-6.0 - toolset: clang compiler: clang++-7 cxxstd: "11,14,17" @@ -85,6 +88,7 @@ jobs: compiler: clang++-8 cxxstd: "11,14,17,2a" os: ubuntu-20.04 + install: clang-8 - toolset: clang compiler: clang++-9 cxxstd: "11,14,17,2a" @@ -123,6 +127,7 @@ jobs: if: matrix.compiler run: | echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam + cat ~/user-config.jam - name: Run tests if: "!matrix.define" From 2ad274cc81f1613a0dc723a6c87a4c48dea9e35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Fri, 2 Apr 2021 20:35:17 +0200 Subject: [PATCH 057/193] ci: Remove cxxstd=14 from clang 3.5 job on GitHub Actions (#590) Despite https://clang.llvm.org/cxx_status.html saying: Clang 3.4 and later implement all of the ISO C++ 2014 standard. The clang 3.5 suffers from the bug in C++14 mode causing failure of Boost.Filesystem build with: error: debug information for auto is not yet supported error: debug information for auto is not yet supported https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=800483 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d77b4dc6b..1e6c356e4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: os: ubuntu-18.04 - toolset: clang compiler: clang++-3.5 - cxxstd: "11,14" + cxxstd: "11" define: "_GLIBCXX_USE_CXX11_ABI=0" os: ubuntu-16.04 install: clang-3.5 From bbdce36a1f655ebf8332b775790673b197c8f1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Schr=C3=B6der?= Date: Sat, 3 Apr 2021 00:46:05 +0200 Subject: [PATCH 058/193] docs: Correct typos in documentation (#592) --- doc/design/color_space.rst | 4 ++-- doc/design/concepts.rst | 2 +- doc/design/point.rst | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/design/color_space.rst b/doc/design/color_space.rst index 413f09c703..e469c4fa02 100644 --- a/doc/design/color_space.rst +++ b/doc/design/color_space.rst @@ -45,7 +45,7 @@ Besides the standard layouts, it also provides: - ``abgr_layout_t`` - ``argb_layout_t`` -As an example, here is how GIL defines the RGBA color space:: +As an example, here is how GIL defines the RGBA color space: .. code-block:: cpp @@ -58,7 +58,7 @@ As an example, here is how GIL defines the RGBA color space:: The ordering of the channels in the color space definition specifies their semantic order. For example, ``red_t`` is the first semantic channel of ``rgba_t``. While there is a unique semantic ordering of the channels in a -color space, channels may vary in their physical ordering in memory +color space, channels may vary in their physical ordering in memory. The mapping of channels is specified by ``ChannelMappingConcept``, which is an MPL random access sequence of integral types. diff --git a/doc/design/concepts.rst b/doc/design/concepts.rst index 47faa7e5d5..7aac361380 100644 --- a/doc/design/concepts.rst +++ b/doc/design/concepts.rst @@ -8,7 +8,7 @@ algorithmic guarantees. For example, GIL class ``pixel`` is a model of GIL ``PixelConcept``. The user may substitute the pixel class with one of their own, and, as long as it satisfies the requirements of ``PixelConcept``, all other GIL classes and algorithms can be used with it. -See more about concepts is avaialble at +See more about concepts at `Generic Programming in ConceptC++ `_ In this document we will use a syntax for defining concepts that is described diff --git a/doc/design/point.rst b/doc/design/point.rst index 035771b50b..e32bca936e 100644 --- a/doc/design/point.rst +++ b/doc/design/point.rst @@ -39,7 +39,7 @@ in which both dimensions are of the same type: typename value_type = axis<0>::type; const value_type& operator[](const T&, size_t i); - value_type& operator[]( T&, size_t i); + value_type& operator[]( T&, size_t i); value_type x,y; }; From 0a21d741ce06ad3a880efb0971eaf7bb51effb78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Thu, 6 May 2021 23:06:24 +0200 Subject: [PATCH 059/193] test: Verify core IO headers are self-contained Looks like skipped by mistake --- test/core/Jamfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/Jamfile b/test/core/Jamfile index c7c03ee376..bc1e5f55d7 100644 --- a/test/core/Jamfile +++ b/test/core/Jamfile @@ -16,7 +16,7 @@ project ; alias headers_concepts : [ generate_self_contained_headers concepts ] ; -alias headers : [ generate_self_contained_headers : concepts extension io ] ; +alias headers : [ generate_self_contained_headers : concepts extension ] ; run promote_integral.cpp ; run test_fixture.cpp ; From 4738a38295b6a153e979a16e0439742c496bd559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 11 May 2021 06:54:39 +0200 Subject: [PATCH 060/193] test: Catch up IO with switch to variant2 migration (#607) See discussion at https://github.com/boostorg/gil/issues/453#issuecomment-833898207 Closes #606 --- test/extension/io/bmp/bmp_old_test.cpp | 6 ++---- test/extension/io/bmp/bmp_test.cpp | 16 +++++++--------- test/extension/io/jpeg/jpeg_old_test.cpp | 16 +++++++--------- test/extension/io/jpeg/jpeg_test.cpp | 6 ++---- test/extension/io/png/png_old_test.cpp | 5 ++--- test/extension/io/png/png_test.cpp | 5 ++--- test/extension/io/pnm/pnm_old_test.cpp | 11 +++++++---- test/extension/io/pnm/pnm_test.cpp | 6 ++---- test/extension/io/raw/raw_test.cpp | 18 ++++++++---------- test/extension/io/targa/targa_old_test.cpp | 7 +++---- test/extension/io/targa/targa_test.cpp | 5 ++--- test/extension/io/tiff/tiff_old_test.cpp | 5 ++--- test/extension/io/tiff/tiff_test.cpp | 8 +++----- 13 files changed, 49 insertions(+), 65 deletions(-) diff --git a/test/extension/io/bmp/bmp_old_test.cpp b/test/extension/io/bmp/bmp_old_test.cpp index bd7fd27d8e..31575de58d 100644 --- a/test/extension/io/bmp/bmp_old_test.cpp +++ b/test/extension/io/bmp/bmp_old_test.cpp @@ -63,15 +63,13 @@ void test_old_write_view() void test_old_dynamic_image() { - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgb8_image_t, gil::rgba8_image_t - >; - - gil::any_image image; + > image; gil::bmp_read_image(bmp_filename.c_str(), image); gil::bmp_write_view(bmp_out + "old_dynamic_image_test.bmp", gil::view(image)); diff --git a/test/extension/io/bmp/bmp_test.cpp b/test/extension/io/bmp/bmp_test.cpp index 6b1385464c..811a7491bc 100644 --- a/test/extension/io/bmp/bmp_test.cpp +++ b/test/extension/io/bmp/bmp_test.cpp @@ -247,15 +247,13 @@ void test_subimage() void test_dynamic_image() { - using my_img_types = mp11::mp_list - < - gil::gray8_image_t, - gil::gray16_image_t, - gil::rgb8_image_t, - gil::rgba8_image_t - >; - - gil::any_image image; + gil::any_image + < + gil::gray8_image_t, + gil::gray16_image_t, + gil::rgb8_image_t, + gil::rgba8_image_t + > image; gil::read_image(bmp_filename.c_str(), image, gil::bmp_tag()); gil::write_view(bmp_out + "dynamic_image_test.bmp", gil::view(image), gil::bmp_tag()); diff --git a/test/extension/io/jpeg/jpeg_old_test.cpp b/test/extension/io/jpeg/jpeg_old_test.cpp index 241b51bc6a..0933bda0b6 100644 --- a/test/extension/io/jpeg/jpeg_old_test.cpp +++ b/test/extension/io/jpeg/jpeg_old_test.cpp @@ -67,15 +67,13 @@ void test_old_write_view() void test_old_dynamic_image() { - using my_img_types = mp11::mp_list - < - gil::gray8_image_t, - gil::gray16_image_t, - gil::rgb8_image_t, - gil::rgba8_image_t - >; - - gil::any_image image; + gil::any_image + < + gil::gray8_image_t, + gil::gray16_image_t, + gil::rgb8_image_t, + gil::rgba8_image_t + > image; gil::jpeg_read_image(jpeg_filename.c_str(), image); #ifdef BOOST_GIL_IO_TEST_ALLOW_WRITING_IMAGES diff --git a/test/extension/io/jpeg/jpeg_test.cpp b/test/extension/io/jpeg/jpeg_test.cpp index be7628180e..42bb6a3fd9 100644 --- a/test/extension/io/jpeg/jpeg_test.cpp +++ b/test/extension/io/jpeg/jpeg_test.cpp @@ -237,15 +237,13 @@ void test_subimage() void test_dynamic_image() { - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgb8_image_t, gil::rgba8_image_t - >; - - gil::any_image image; + > image; gil::read_image(jpeg_filename.c_str(), image, gil::jpeg_tag()); #ifdef BOOST_GIL_IO_TEST_ALLOW_WRITING_IMAGES diff --git a/test/extension/io/png/png_old_test.cpp b/test/extension/io/png/png_old_test.cpp index 869f749e09..fc5de954b9 100644 --- a/test/extension/io/png/png_old_test.cpp +++ b/test/extension/io/png/png_old_test.cpp @@ -67,14 +67,13 @@ void test_old_write_view() void test_old_dynamic_image() { - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgb8_image_t, gil::rgba8_image_t - >; - gil::any_image image; + > image; gil::png_read_image(png_filename.c_str(), image); diff --git a/test/extension/io/png/png_test.cpp b/test/extension/io/png/png_test.cpp index 937c4bbe09..de902c56f7 100644 --- a/test/extension/io/png/png_test.cpp +++ b/test/extension/io/png/png_test.cpp @@ -265,14 +265,13 @@ void test_subimage() void test_dynamic_image() { - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgb8_image_t, gil::rgba8_image_t - >; - gil::any_image image; + > image; gil::read_image(png_filename.c_str(), image, gil::png_tag()); diff --git a/test/extension/io/pnm/pnm_old_test.cpp b/test/extension/io/pnm/pnm_old_test.cpp index 21e07fe3e3..ae10b3aec9 100644 --- a/test/extension/io/pnm/pnm_old_test.cpp +++ b/test/extension/io/pnm/pnm_old_test.cpp @@ -66,10 +66,13 @@ void test_old_write_view() void test_old_dynamic_image() { - using my_img_types = - mp11::mp_list; - - gil::any_image image; + gil::any_image + < + gil::gray8_image_t, + gil::gray16_image_t, + gil::rgb8_image_t, + gil::gray1_image_t + > image; gil::pnm_read_image(pnm_filename.c_str(), image); diff --git a/test/extension/io/pnm/pnm_test.cpp b/test/extension/io/pnm/pnm_test.cpp index 2e398343ea..7ccd7d1bad 100644 --- a/test/extension/io/pnm/pnm_test.cpp +++ b/test/extension/io/pnm/pnm_test.cpp @@ -204,15 +204,13 @@ void test_subimage() void test_dynamic_image_test() { - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgb8_image_t, gil::gray1_image_t - >; - - gil::any_image image; + > image; gil::read_image(pnm_filename.c_str(), image, gil::pnm_tag()); diff --git a/test/extension/io/raw/raw_test.cpp b/test/extension/io/raw/raw_test.cpp index 029eef07fe..810ebf0b91 100644 --- a/test/extension/io/raw/raw_test.cpp +++ b/test/extension/io/raw/raw_test.cpp @@ -97,16 +97,14 @@ void test_subimage() void test_dynamic_image() { - using my_img_types = mp11::mp_list - < - gil::gray8_image_t, - gil::gray16_image_t, - gil::rgb8_image_t, - gil::rgba8_image_t - >; - - gil::any_image image; - gil::read_image(raw_filename.c_str(), image, gil::raw_tag()); + gil::any_image + < + gil::gray8_image_t, + gil::gray16_image_t, + gil::rgb8_image_t, + gil::rgba8_image_t + > image; + gil::read_image(raw_filename.c_str(), image, gil::raw_tag()); } int main() diff --git a/test/extension/io/targa/targa_old_test.cpp b/test/extension/io/targa/targa_old_test.cpp index cfb7aa9a78..b1f4bcbcb5 100644 --- a/test/extension/io/targa/targa_old_test.cpp +++ b/test/extension/io/targa/targa_old_test.cpp @@ -64,15 +64,14 @@ void test_old_write_view() void test_old_dynamic_image() { - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgb8_image_t, gil::rgba8_image_t - >; - - gil::any_image image; + > image; + gil::targa_read_image(targa_filename.c_str(), image); targa_write_view(targa_out + "old_dynamic_image_test.tga", gil::view(image)); diff --git a/test/extension/io/targa/targa_test.cpp b/test/extension/io/targa/targa_test.cpp index bb1d989350..c94e102244 100644 --- a/test/extension/io/targa/targa_test.cpp +++ b/test/extension/io/targa/targa_test.cpp @@ -254,15 +254,14 @@ void test_subimage() void test_dynamic_image() { - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgb8_image_t, gil::rgba8_image_t - >; + > image; - gil::any_image image; gil::read_image(targa_filename.c_str(), image, gil::targa_tag()); gil::write_view(targa_out + "dynamic_image_test.tga", gil::view(image), gil::targa_tag()); } diff --git a/test/extension/io/tiff/tiff_old_test.cpp b/test/extension/io/tiff/tiff_old_test.cpp index 326a75c18c..3d1c42bb1e 100644 --- a/test/extension/io/tiff/tiff_old_test.cpp +++ b/test/extension/io/tiff/tiff_old_test.cpp @@ -60,14 +60,13 @@ void test_old_read_and_convert_view() void test_old_dynamic_image() { - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgba8_image_t, gil::gray1_image_t - >; - gil::any_image image; + > image; gil::tiff_read_image(tiff_filename.c_str(), image); diff --git a/test/extension/io/tiff/tiff_test.cpp b/test/extension/io/tiff/tiff_test.cpp index f0c9d9972c..f8ee2f0b09 100644 --- a/test/extension/io/tiff/tiff_test.cpp +++ b/test/extension/io/tiff/tiff_test.cpp @@ -267,15 +267,13 @@ void test_dynamic_image() { // FIXME: This test has been disabled for now because of compilation issues with MSVC10. - using my_img_types = mp11::mp_list + gil::any_image < gil::gray8_image_t, gil::gray16_image_t, gil::rgb8_image_t, - gil::rgba8_image_t, - gil::gray1_image_t - >; - gil::any_image image; + gil::rgba8_image_t + > image; gil::read_image(tiff_filename.c_str(), image, gil::tiff_tag()); From d65cc582254de7065b9b2e795786f3d561a46398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sun, 23 May 2021 13:24:38 +0200 Subject: [PATCH 061/193] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 91613c08c5..7520b87a9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,8 @@ please follow the workflow explained in this document. ## Pull Requests + +- **DO** all your work in fork this repository. - **DO** base your work against the `develop` branch, not the `master`. - **DO** submit all major changes to code via pull requests (PRs) rather than through a direct commit. PRs will be CI-checked first, then reviewed and potentially merged From 7dd61209a81695600d773f481bb8b22b0f74a30a Mon Sep 17 00:00:00 2001 From: Prathamesh Tagore <63031630+meshtag@users.noreply.github.com> Date: Sun, 30 May 2021 23:51:46 +0530 Subject: [PATCH 062/193] Add remaining pieces of documentation in convolve.hpp (#611) --- .../boost/gil/extension/numeric/convolve.hpp | 96 ++++++++++++------- 1 file changed, 61 insertions(+), 35 deletions(-) diff --git a/include/boost/gil/extension/numeric/convolve.hpp b/include/boost/gil/extension/numeric/convolve.hpp index a401b0028e..de5732630f 100644 --- a/include/boost/gil/extension/numeric/convolve.hpp +++ b/include/boost/gil/extension/numeric/convolve.hpp @@ -31,17 +31,22 @@ namespace boost { namespace gil { namespace detail { -/// \brief Compute the cross-correlation of 1D kernel with the rows of an image -/// \tparam PixelAccum - TODO -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel - TODO -/// \tparam DstView Models MutableImageViewConcept -/// \tparam Correlator - TODO -/// \param src_view -/// \param kernel - TODO -/// \param dst_view Destination where new computed values of pixels are assigned to -/// \param option - TODO -/// \param correlator - TODO +/// \brief Computes the cross-correlation of 1D kernel with rows of an image. +/// \tparam PixelAccum - Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView - Specifies the type of gil view of source image which is to be row correlated +/// with the kernel. +/// \tparam Kernel - Specifies the type of 1D kernel which will be row correlated with source image. +/// \tparam DstView - Specifies the type of gil view which will store the result of row +/// correlation between source image and kernel. +/// \tparam Correlator - Specifies the type of correlator which should be used for performing +/// correlation. +/// \param src_view - Gil view of source image used in correlation. +/// \param kernel - 1D kernel which will be correlated with source image. +/// \param dst_view - Gil view which will store the result of row correlation between "src_view" +/// and "kernel". +/// \param option - Specifies the manner in which boundary pixels of "dst_view" should be computed. +/// \param correlator - Correlator which will be used for performing correlation. template < typename PixelAccum, @@ -147,6 +152,9 @@ void correlate_rows_impl( } } +/// \brief Provides functionality for performing 1D correlation between the kernel and a buffer +/// storing row pixels of source image. Kernel size is to be provided through constructor for all +/// instances. template class correlator_n { @@ -167,6 +175,9 @@ class correlator_n std::size_t size_{0}; }; +/// \brief Provides functionality for performing 1D correlation between the kernel and a buffer +/// storing row pixels of source image. Kernel size is a template parameter and must be +/// compulsorily specified while using. template struct correlator_k { @@ -184,10 +195,12 @@ struct correlator_k } // namespace detail /// \ingroup ImageAlgorithms -/// \brief Correlate 1D variable-size kernel along the rows of image -/// \tparam PixelAccum TODO +/// \brief Correlate 1D variable-size kernel along the rows of image. +/// \tparam PixelAccum Specifies tha data type which will be used while creating buffer container +/// which is utilized for holding source image pixels after applying appropriate boundary +/// manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be row correlated with source image. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -202,10 +215,12 @@ void correlate_rows( } /// \ingroup ImageAlgorithms -/// \brief Correlate 1D variable-size kernel along the columns of image -/// \tparam PixelAccum TODO +/// \brief Correlates 1D variable-size kernel along the columns of image. +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be column correlated with source +/// image. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -220,10 +235,11 @@ void correlate_cols( } /// \ingroup ImageAlgorithms -/// \brief Convolve 1D variable-size kernel along the rows of image -/// \tparam PixelAccum TODO +/// \brief Convolves 1D variable-size kernel along the rows of image. +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be row convoluted with source image. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -237,10 +253,12 @@ void convolve_rows( } /// \ingroup ImageAlgorithms -/// \brief Convolve 1D variable-size kernel along the columns of image -/// \tparam PixelAccum TODO +/// \brief Convolves 1D variable-size kernel along the columns of image. +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be column convoluted with source +/// image. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -255,10 +273,11 @@ void convolve_cols( } /// \ingroup ImageAlgorithms -/// \brief Correlate 1D fixed-size kernel along the rows of image -/// \tparam PixelAccum TODO +/// \brief Correlate 1D fixed-size kernel along the rows of image. +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be row correlated with source image. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -274,9 +293,11 @@ void correlate_rows_fixed( /// \ingroup ImageAlgorithms /// \brief Correlate 1D fixed-size kernel along the columns of image -/// \tparam PixelAccum TODO +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be column correlated with source +/// image. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -292,9 +313,10 @@ void correlate_cols_fixed( /// \ingroup ImageAlgorithms /// \brief Convolve 1D fixed-size kernel along the rows of image -/// \tparam PixelAccum TODO +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be row convolved with source image. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -309,9 +331,11 @@ void convolve_rows_fixed( /// \ingroup ImageAlgorithms /// \brief Convolve 1D fixed-size kernel along the columns of image -/// \tparam PixelAccum TODO +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be column convolved with source +/// image. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -330,9 +354,11 @@ namespace detail /// \ingroup ImageAlgorithms /// \brief Convolve 1D variable-size kernel along both rows and columns of image -/// \tparam PixelAccum TODO +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 1D kernel which will be used for 1D row and column +/// convolution. /// \tparam DstView Models MutableImageViewConcept template BOOST_FORCEINLINE @@ -388,7 +414,7 @@ void convolve_2d_impl(SrcView const& src_view, DstView const& dst_view, Kernel c /// this is the default option and cannot be changed for now /// (In future there are plans to improve the algorithm and allow user to use other options as well) /// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel TODO +/// \tparam Kernel Specifies the type of 2D kernel which will be used while convolution. /// \tparam DstView Models MutableImageViewConcept template void convolve_2d(SrcView const& src_view, Kernel const& kernel, DstView const& dst_view) From effd078b325c1c5f408a9ae87bd695b429875d8e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 11 Jun 2021 11:22:50 +0200 Subject: [PATCH 063/193] Fix C++20 incompatibilities in IO extensions --- include/boost/gil/extension/io/bmp/tags.hpp | 2 +- include/boost/gil/extension/io/jpeg/tags.hpp | 2 +- include/boost/gil/extension/io/png/tags.hpp | 6 +++--- include/boost/gil/extension/io/pnm/tags.hpp | 2 +- include/boost/gil/extension/io/raw/tags.hpp | 2 +- include/boost/gil/extension/io/targa/tags.hpp | 2 +- include/boost/gil/extension/io/tiff/tags.hpp | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/boost/gil/extension/io/bmp/tags.hpp b/include/boost/gil/extension/io/bmp/tags.hpp index 37921e1922..5dfcc6892c 100644 --- a/include/boost/gil/extension/io/bmp/tags.hpp +++ b/include/boost/gil/extension/io/bmp/tags.hpp @@ -73,7 +73,7 @@ template<> struct image_read_info< bmp_tag > { /// Default contructor. - image_read_info< bmp_tag >() + image_read_info() : _top_down(false) , _valid( false ) {} diff --git a/include/boost/gil/extension/io/jpeg/tags.hpp b/include/boost/gil/extension/io/jpeg/tags.hpp index a12c237df6..e0db071b25 100644 --- a/include/boost/gil/extension/io/jpeg/tags.hpp +++ b/include/boost/gil/extension/io/jpeg/tags.hpp @@ -141,7 +141,7 @@ template<> struct image_read_settings< jpeg_tag > : public image_read_settings_base { /// Default constructor - image_read_settings() + image_read_settings() : image_read_settings_base() , _dct_method( jpeg_dct_method::default_value ) {} diff --git a/include/boost/gil/extension/io/png/tags.hpp b/include/boost/gil/extension/io/png/tags.hpp index d2fa4ddeda..3c725be037 100644 --- a/include/boost/gil/extension/io/png/tags.hpp +++ b/include/boost/gil/extension/io/png/tags.hpp @@ -573,7 +573,7 @@ template<> struct image_read_info< png_tag > : public png_info_base { /// Default constructor. - image_read_info< png_tag >() + image_read_info() : png_info_base() {} }; @@ -669,7 +669,7 @@ struct image_read_settings< png_tag > : public image_read_settings_base , public png_read_settings_base { /// Default Constructor - image_read_settings< png_tag >() + image_read_settings() : image_read_settings_base() , png_read_settings_base() , _screen_gamma( 1.0 ) @@ -708,7 +708,7 @@ struct image_read_settings< png_tag > : public image_read_settings_base , public png_read_settings_base { /// Default Constructor. - image_read_settings< png_tag >() + image_read_settings() : image_read_settings_base() , png_read_settings_base() , _apply_screen_gamma( false ) diff --git a/include/boost/gil/extension/io/pnm/tags.hpp b/include/boost/gil/extension/io/pnm/tags.hpp index 8623e014fc..a3ce0ff005 100644 --- a/include/boost/gil/extension/io/pnm/tags.hpp +++ b/include/boost/gil/extension/io/pnm/tags.hpp @@ -65,7 +65,7 @@ template<> struct image_read_settings< pnm_tag > : public image_read_settings_base { /// Default constructor - image_read_settings< pnm_tag >() + image_read_settings() : image_read_settings_base() {} diff --git a/include/boost/gil/extension/io/raw/tags.hpp b/include/boost/gil/extension/io/raw/tags.hpp index f8e18a5040..e6912a6b35 100644 --- a/include/boost/gil/extension/io/raw/tags.hpp +++ b/include/boost/gil/extension/io/raw/tags.hpp @@ -123,7 +123,7 @@ template<> struct image_read_info< raw_tag > { /// Default contructor. - image_read_info< raw_tag >() + image_read_info() : _valid( false ) {} diff --git a/include/boost/gil/extension/io/targa/tags.hpp b/include/boost/gil/extension/io/targa/tags.hpp index f56a1e20b4..525101079c 100644 --- a/include/boost/gil/extension/io/targa/tags.hpp +++ b/include/boost/gil/extension/io/targa/tags.hpp @@ -77,7 +77,7 @@ template<> struct image_read_info< targa_tag > { /// Default contructor. - image_read_info< targa_tag >() + image_read_info() : _screen_origin_bit(false) , _valid( false ) {} diff --git a/include/boost/gil/extension/io/tiff/tags.hpp b/include/boost/gil/extension/io/tiff/tags.hpp index 4bee8bb978..cae09c1afa 100644 --- a/include/boost/gil/extension/io/tiff/tags.hpp +++ b/include/boost/gil/extension/io/tiff/tags.hpp @@ -270,7 +270,7 @@ template<> struct image_read_settings< tiff_tag > : public image_read_settings_base { /// Default constructor - image_read_settings< tiff_tag >() + image_read_settings() : image_read_settings_base() , _directory( tiff_directory::default_value::value ) {} From 2cc525a2d6a48ba7bc6be44626d9d82bafa4318d Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Fri, 16 Jul 2021 17:18:11 +0200 Subject: [PATCH 064/193] Fix for_each_pixel for non 1d iterable views --- include/boost/gil/algorithm.hpp | 3 +- test/core/algorithm/for_each_pixel.cpp | 41 ++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/include/boost/gil/algorithm.hpp b/include/boost/gil/algorithm.hpp index cbd1716868..791ac291fa 100644 --- a/include/boost/gil/algorithm.hpp +++ b/include/boost/gil/algorithm.hpp @@ -874,7 +874,8 @@ F for_each_pixel(View const& view, F fun) else { for (std::ptrdiff_t y = 0; y < view.height(); ++y) - std::for_each(view.row_begin(y), view.row_end(y), fun); + for (auto begin = view.row_begin(y), end = view.row_end(y); begin != end; ++begin) + fun(*begin); return fun; } } diff --git a/test/core/algorithm/for_each_pixel.cpp b/test/core/algorithm/for_each_pixel.cpp index 5490b27f3a..25eb9c5cfd 100644 --- a/test/core/algorithm/for_each_pixel.cpp +++ b/test/core/algorithm/for_each_pixel.cpp @@ -7,11 +7,10 @@ // #include #include +#include #include -#include "test_utility_output_stream.hpp" - namespace gil = boost::gil; void test_lambda_expression() @@ -26,9 +25,47 @@ void test_lambda_expression() BOOST_TEST_EQ(sum, 2 * 2 * 128); } +struct accumulator +{ + void operator()(gil::gray8_pixel_t const& p) { + sum += gil::at_c<0>(p); + } + + int sum = 0; +}; + +void test_function_object_1d_traversable() +{ + gil::gray8_pixel_t const gray128(128); + gil::gray8_image_t image(2, 2, gray128); + + accumulator acc; + acc = gil::for_each_pixel( + gil::const_view(image), + acc + ); + BOOST_TEST_EQ(acc.sum, 2 * 2 * 128); +} + + +void test_function_object_not_1d_traversable() +{ + gil::gray8_pixel_t const gray128(128); + gil::gray8_image_t image(4, 4, gray128); + + accumulator acc; + acc = gil::for_each_pixel( + gil::subimage_view(gil::const_view(image), gil::point_t{1, 1}, gil::point_t{2, 2}), + acc + ); + BOOST_TEST_EQ(acc.sum, 2 * 2 * 128); +} + int main() { test_lambda_expression(); + test_function_object_1d_traversable(); + test_function_object_not_1d_traversable(); return ::boost::report_errors(); } From 3279523252327ac7a99296806c5a3615485955a3 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Mon, 4 Oct 2021 19:03:14 +0200 Subject: [PATCH 065/193] Update pixel design documentation for MP11 changes (#626) Update documentation after #274. --- doc/design/pixel.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/design/pixel.rst b/doc/design/pixel.rst index 1860a7d3c4..8f18fec14e 100644 --- a/doc/design/pixel.rst +++ b/doc/design/pixel.rst @@ -183,13 +183,13 @@ such packed pixel formats: .. code-block:: cpp // define an rgb565 pixel - typedef packed_pixel_type, rgb_layout_t>::type rgb565_pixel_t; + typedef packed_pixel_type, rgb_layout_t>::type rgb565_pixel_t; function_requires >(); static_assert(sizeof(rgb565_pixel_t) == 2, ""); // define a bgr556 pixel - typedef packed_pixel_type, bgr_layout_t>::type bgr556_pixel_t; + typedef packed_pixel_type, bgr_layout_t>::type bgr556_pixel_t; function_requires >(); @@ -211,7 +211,7 @@ pixels and pixel iterators: .. code-block:: cpp // Mutable reference to a BGR232 pixel - typedef const bit_aligned_pixel_reference, bgr_layout_t, true> bgr232_ref_t; + typedef const bit_aligned_pixel_reference, bgr_layout_t, true> bgr232_ref_t; // A mutable iterator over BGR232 pixels typedef bit_aligned_pixel_iterator bgr232_ptr_t; @@ -255,7 +255,7 @@ algorithms and metafunctions of color bases can work with them as well: BOOST_MPL_ASSERT(num_channels::value == 3); BOOST_MPL_ASSERT((is_same::type, bits8>)); BOOST_MPL_ASSERT((is_same::type, rgb_t> )); - BOOST_MPL_ASSERT((is_same::type, mpl::vector3_c > )); + BOOST_MPL_ASSERT((is_same::type, mp11::mp_list_c > )); // Pixels contain just the three channels and nothing extra BOOST_MPL_ASSERT(sizeof(rgb8_pixel_t)==3); From a328bef94de26b15ec676cf53d4c58fecf0d7dab Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sun, 31 Oct 2021 00:14:15 +0300 Subject: [PATCH 066/193] Add a Boost-friendly subproject case to CMakeLists (#614) --- CMakeLists.txt | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fc5e60123b..f0f777bd5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,50 @@ # # Copyright (c) 2017-2019 Mateusz Loskot +# Copyright (c) 2020-2021 Peter Dimov # # Distributed under 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) # + +if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + +# Generated by `boostdep --cmake gil` + +cmake_minimum_required(VERSION 3.8...3.20) + +project(boost_gil VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) + +add_library(boost_gil INTERFACE) +add_library(Boost::gil ALIAS boost_gil) + +target_include_directories(boost_gil INTERFACE include) + +target_link_libraries(boost_gil + INTERFACE + Boost::assert + Boost::concept_check + Boost::config + Boost::container_hash + Boost::core + Boost::filesystem + Boost::integer + Boost::iterator + Boost::mp11 + Boost::preprocessor + Boost::type_traits + Boost::variant2 +) + +target_compile_features(boost_gil INTERFACE cxx_std_11) + +else() + # **WARNING:** # The CMake configuration is only provided for convenience # of contributors. It does not export or install any targets, # deploy config files or support subproject workflow. -# + cmake_minimum_required(VERSION 3.10) #----------------------------------------------------------------------------- @@ -250,3 +285,5 @@ endif() if(BOOST_GIL_BUILD_EXAMPLES AND BOOST_GIL_ENABLE_EXT_IO) add_subdirectory(example) endif() + +endif() From b6526deb7862ffb10ec0da4739402723f7604a47 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Mon, 8 Nov 2021 19:25:53 +0100 Subject: [PATCH 067/193] Remove remaining references to MPL in the documentation (#631) --- doc/design/color_base.rst | 2 +- doc/design/color_space.rst | 10 +++++----- doc/design/metafunctions.rst | 2 +- doc/design/pixel_iterator.rst | 2 +- doc/io.rst | 25 ++++++++++--------------- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/doc/design/color_base.rst b/doc/design/color_base.rst index c39cb7aa31..f774d2d62c 100644 --- a/doc/design/color_base.rst +++ b/doc/design/color_base.rst @@ -132,7 +132,7 @@ bases: .. code-block:: cpp - // Metafunction returning an mpl::int_ equal to the number of elements in the color base + // Metafunction returning an integral constant equal to the number of elements in the color base template struct size; // Returns the type of the return value of semantic_at_c(color_base) diff --git a/doc/design/color_space.rst b/doc/design/color_space.rst index e469c4fa02..d4690e3143 100644 --- a/doc/design/color_space.rst +++ b/doc/design/color_space.rst @@ -53,7 +53,7 @@ As an example, here is how GIL defines the RGBA color space: struct green_t {}; struct blue_t {}; struct alpha_t {}; - rgba_t = using mpl::vector4; + rgba_t = using mp11::mp_list; The ordering of the channels in the color space definition specifies their semantic order. For example, ``red_t`` is the first semantic channel of @@ -71,7 +71,7 @@ Thus they are grouped in GIL's layout: template < typename ColorSpace, - typename ChannelMapping = mpl::range_c::value> + typename ChannelMapping = mp11::make_integer_sequence::value> > struct layout { @@ -84,6 +84,6 @@ Here is how to create layouts for the RGBA color space: .. code-block:: cpp using rgba_layout_t = layout; // default ordering is 0,1,2,3... - using bgra_layout_t = layout>; - using argb_layout_t = layout>; - using abgr_layout_t = layout>; + using bgra_layout_t = layout>; + using argb_layout_t = layout>; + using abgr_layout_t = layout>; diff --git a/doc/design/metafunctions.rst b/doc/design/metafunctions.rst index 1875f822c1..21a9a55bda 100644 --- a/doc/design/metafunctions.rst +++ b/doc/design/metafunctions.rst @@ -209,7 +209,7 @@ just like ``View``, but being grayscale and planar: .. code-block:: cpp - using VT = typename derived_view_type::type; + using VT = typename derived_view_type::type; Type traits ----------- diff --git a/doc/design/pixel_iterator.rst b/doc/design/pixel_iterator.rst index 877c7a95d7..35fde992c3 100644 --- a/doc/design/pixel_iterator.rst +++ b/doc/design/pixel_iterator.rst @@ -101,7 +101,7 @@ type, and a metafunction to rebind to another base iterator: concept IteratorAdaptorConcept { - where SameType::type, mpl::true_>; + where SameType::type, mp11::mp_true>; typename iterator_adaptor_get_base; where Metafunction >; diff --git a/doc/io.rst b/doc/io.rst index 0cc920ca03..29736e9ef1 100644 --- a/doc/io.rst +++ b/doc/io.rst @@ -240,13 +240,11 @@ several image types. The IO extension would then pick the matching image type to the current image file. The following example shows this feature:: - typedef mpl::vector< gray8_image_t - , gray16_image_t - , rgb8_image_t - , rgba_image_t - > my_img_types; - - any_image< my_img_types > runtime_image; + any_image< gray8_image_t + , gray16_image_t + , rgb8_image_t + , rgba8_image_t + > my_img_types > runtime_image; read_image( filename , runtime_image @@ -319,14 +317,11 @@ the possible settings. Writing an any_image<...> is supported. See the following example:: - typedef mpl::vector< gray8_image_t - , gray16_image_t - , rgb8_image_t - , rgba_image_t - > my_img_types; - - - any_image< my_img_types > runtime_image; + any_image< gray8_image_t + , gray16_image_t + , rgb8_image_t + , rgba8_image_t + > my_img_types > runtime_image; // fill any_image From 0b24f4cdbf430430b5430507822b0698cd9d2ac7 Mon Sep 17 00:00:00 2001 From: Nicolas Herry Date: Wed, 10 Nov 2021 18:21:02 +0100 Subject: [PATCH 068/193] Ensure all examples build without error (#628) * Added all missing examples, dodgy Jamfile still * Fixed attributions and Jamfile indent * One readme per example: synopsis, build and exec reqs * Cleaned up example/convolve2d.cpp * Added example target to root Jamfile Closes #436 --- Jamfile | 1 + example/Jamfile | 20 ++++++++--- example/README.md | 35 ++---------------- example/adaptive_he.cpp | 7 ++++ example/adaptive_he.md | 20 +++++++++++ example/adaptive_threshold.cpp | 10 ++++++ example/adaptive_threshold.md | 23 ++++++++++++ example/affine.cpp | 7 +++- example/affine.md | 19 ++++++++++ example/anisotropic_diffusion.cpp | 7 ++++ example/anisotropic_diffusion.md | 22 ++++++++++++ example/convolution.cpp | 14 +++++++- example/convolution.md | 19 ++++++++++ example/convolve2d.cpp | 36 ++++++++++++------- example/convolve2d.md | 22 ++++++++++++ example/dynamic_image.cpp | 5 +++ example/dynamic_image.md | 19 ++++++++++ example/harris.cpp | 7 ++-- example/harris.md | 21 +++++++++++ example/hessian.cpp | 10 ++++++ example/hessian.md | 20 +++++++++++ example/histogram.cpp | 13 ++++--- example/histogram.md | 20 +++++++++++ example/histogram_equalization.cpp | 15 ++++++++ example/histogram_equalization.md | 19 ++++++++++ example/histogram_matching.cpp | 7 ++++ example/histogram_matching.md | 19 ++++++++++ example/hough_transform_circle.cpp | 12 ++++++- example/hough_transform_circle.md | 18 ++++++++++ example/hough_transform_line.cpp | 13 +++++-- example/hough_transform_line.md | 18 ++++++++++ example/hvstack.hpp | 8 +++++ example/interleaved_ptr.cpp | 6 +++- example/interleaved_ptr.hpp | 1 + example/interleaved_ptr.md | 19 ++++++++++ example/interleaved_ref.hpp | 1 + example/mandelbrot.cpp | 7 +++- example/mandelbrot.md | 19 ++++++++++ example/morphology.cpp | 15 ++++++++ example/morphology.md | 31 ++++++++++++++++ example/packed_pixel.cpp | 1 + example/packed_pixel.md | 19 ++++++++++ example/rasterizer_circle.cpp | 11 +++++- example/rasterizer_circle.md | 19 ++++++++++ example/rasterizer_ellipse.cpp | 12 +++++++ example/rasterizer_ellipse.md | 22 ++++++++++++ example/rasterizer_line.cpp | 12 ++++++- example/rasterizer_line.md | 23 ++++++++++++ example/resize.cpp | 6 +++- example/resize.md | 19 ++++++++++ example/sobel_scharr.cpp | 13 +++++++ example/sobel_scharr.md | 20 +++++++++++ example/threshold.cpp | 15 ++++++++ example/threshold.md | 19 ++++++++++ example/tutorial_histogram.cpp | 4 ++- example/tutorial_histogram.md | 19 ++++++++++ example/x_gradient.cpp | 11 +++++- example/x_gradient.md | 19 ++++++++++ .../adaptive_histogram_equalization.hpp | 2 -- .../image_processing/histogram_matching.hpp | 2 -- .../gil/image_processing/hough_transform.hpp | 2 -- 61 files changed, 799 insertions(+), 76 deletions(-) create mode 100644 example/adaptive_he.md create mode 100644 example/adaptive_threshold.md create mode 100644 example/affine.md create mode 100644 example/anisotropic_diffusion.md create mode 100644 example/convolution.md create mode 100644 example/convolve2d.md create mode 100644 example/dynamic_image.md create mode 100644 example/harris.md create mode 100644 example/hessian.md create mode 100644 example/histogram.md create mode 100644 example/histogram_equalization.md create mode 100644 example/histogram_matching.md create mode 100644 example/hough_transform_circle.md create mode 100644 example/hough_transform_line.md create mode 100644 example/interleaved_ptr.md create mode 100644 example/mandelbrot.md create mode 100644 example/morphology.md create mode 100644 example/packed_pixel.md create mode 100644 example/rasterizer_circle.md create mode 100644 example/rasterizer_ellipse.md create mode 100644 example/rasterizer_line.md create mode 100644 example/resize.md create mode 100644 example/sobel_scharr.md create mode 100644 example/threshold.md create mode 100644 example/tutorial_histogram.md create mode 100644 example/x_gradient.md diff --git a/Jamfile b/Jamfile index 739260ce92..ee18a21f23 100644 --- a/Jamfile +++ b/Jamfile @@ -7,4 +7,5 @@ # LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # please order by name to ease maintenance +build-project example ; build-project test ; diff --git a/example/Jamfile b/example/Jamfile index 3ce548cff5..bf05e8323e 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -11,6 +11,7 @@ import regex ; import testing ; using libjpeg : : : : true ; # work around bug on master +using libpng : : : : true ; project : # requirements @@ -19,6 +20,7 @@ project # TODO: Add missing examples local sources = + adaptive_he.cpp adaptive_threshold.cpp affine.cpp anisotropic_diffusion.cpp @@ -27,12 +29,18 @@ local sources = dynamic_image.cpp harris.cpp hessian.cpp + histogram_equalization.cpp + histogram_matching.cpp histogram.cpp + hough_transform_circle.cpp + hough_transform_line.cpp interleaved_ptr.cpp mandelbrot.cpp morphology.cpp packed_pixel.cpp + rasterizer_circle.cpp rasterizer_ellipse.cpp + rasterizer_line.cpp resize.cpp sobel_scharr.cpp threshold.cpp @@ -44,11 +52,13 @@ local targets ; for local s in $(sources) { - targets += - [ compile $(s) : - [ ac.check-library /libjpeg//libjpeg : /libjpeg//libjpeg : no ] - ] - ; +targets += + [ exe [ regex.replace $(s) ".cpp" "" ] : + $(s) + /libjpeg//libjpeg + /libpng//libpng + ] + ; } alias examples : $(targets) ; diff --git a/example/README.md b/example/README.md index cee1770888..53a61209b4 100644 --- a/example/README.md +++ b/example/README.md @@ -3,6 +3,7 @@ This directory contains - examples of C++ programs using GIL +- a documentation file describing the synopsis, build and execution requirements for each example - configuration files for Boost.Build command line and CMake integration for popular IDEs. We provide Boost.Build (`Jamfile`) and CMake (`CMakeLists.txt`) @@ -10,36 +11,4 @@ configurations to build the examples. See the [CONTRIBUTING.md](../CONTRIBUTING.md) for details on how to run `b2` and `cmake` for Boost.GIL. -Each example is build as a separate executable. -Each executable generates its output as `out-.jpg`. -For example, the `resize.cpp` example generates the image `out-resize.jpg`. - -The following C++ examples are included: - -1. `resize.cpp` - Scales an image using bilinear or nearest-neighbour resampling. - -2. `affine.cpp` - Performs an arbitrary affine transformation on the image. - -3. `convolution.cpp` - Convolves the image with a Gaussian kernel. - -4. `mandelbrot.cpp` - Creates a synthetic image defining the Mandelbrot set. - -5. `interleaved_ptr.cpp` - Illustrates how to create a custom pixel reference and iterator. - Creates a GIL image view over user-supplied data without the need to cast to GIL pixel type. - -6. `x_gradient.cpp` - Horizontal gradient, from the tutorial - -7. `histogram.cpp` - Algorithm to compute the histogram of an image - -8. `packed_pixel.cpp` - Illustrates how to create a custom pixel model - a pixel whose channel size is not divisible by bytes. - -9. `dynamic_image.cpp` - Example of using images whose type is instantiated at run time. +Each example is built as a separate executable. diff --git a/example/adaptive_he.cpp b/example/adaptive_he.cpp index 0610932c98..3a0e23edc6 100644 --- a/example/adaptive_he.cpp +++ b/example/adaptive_he.cpp @@ -13,6 +13,13 @@ using namespace boost::gil; +// Demonstrates Adaptive Histogram Equalization (AHE) + +// See also: +// histogram.cpp - General use of histograms in GIL +// histogram_equalization.cpp - Regular Histogram Equalization +// histogram_matching.cpp - Reference-based histogram computation + int main() { gray8_image_t img; diff --git a/example/adaptive_he.md b/example/adaptive_he.md new file mode 100644 index 0000000000..570d160957 --- /dev/null +++ b/example/adaptive_he.md @@ -0,0 +1,20 @@ +# Adaptive Histogram Equalization + +Adaptive Histogram Equalization (AHE) capabilities in GIL are demonstrated by the program `adaptive_he`, compiled from the sources `example/adaptive_he.cpp`. + +## Synopsis +`adaptive_he` + +The program doesn't take any argument on the command line. + +`adaptive_he` expects to find an image called `test_adaptive.png` in the current directory, and produces the image `out-adaptive.png` in return. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +- `adaptive_he` expects to find an image called `test_adaptive.png` in the current directory. + diff --git a/example/adaptive_threshold.cpp b/example/adaptive_threshold.cpp index 65f0f9ac14..3d0f650acd 100644 --- a/example/adaptive_threshold.cpp +++ b/example/adaptive_threshold.cpp @@ -5,12 +5,22 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include #include using namespace boost::gil; +// Demonstrates Adaptive Thresholding + +// threshold_adaptive works following either the mean or the gaussian method, and accepts also a direction +// The direction indicates whether the pixels are assigned the max value when their values are greater +// than the threshold (regular), or when they are less than the threshold (inverse) +// threshold_adaptive is defined in include/boost/gil/image_processing/threshold.hpp +// See also: +// threshold.cpp - Simple thresholding + int main() { gray8_image_t img; diff --git a/example/adaptive_threshold.md b/example/adaptive_threshold.md new file mode 100644 index 0000000000..a005307786 --- /dev/null +++ b/example/adaptive_threshold.md @@ -0,0 +1,23 @@ +# Adaptive Thresholding + +Adaptive Thresholding capabilities in GIL are demonstrated by the program `adaptive_threshold`, compiled from the sources `example/adaptive_threshold.cpp`. + +## Synopsis +`adaptive_threshold` + +The program doesn't take any argument on the command line. + +`adaptive_threshold` expects to find an image called `test_adaptive.png` in the current directory, and produces one image for each thresholding method: +- `out-threshold-adaptive-mean.png` +- `out-threshold-adaptive-mean-inv.png` +- `out-threshold-adaptive-gaussian.png` +- `out-threshold-adaptive-gaussian-inv.png`. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +- `adaptive_threshold` expects to find an image called `test_adaptive.png` in the current directory. diff --git a/example/affine.cpp b/example/affine.cpp index a54cfaa1d1..a078d17a2d 100644 --- a/example/affine.cpp +++ b/example/affine.cpp @@ -5,12 +5,17 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #include #include #include #include -// Example for resample_pixels() in the numeric extension +// Performs an arbitrary affine transformation on the image. + +// This example relies on the matrices and functions available in GIL to define the operation, +// in include/boost/gil/extension/numeric/affine.hpp +// and calls resample_pixels(), avaiable in the numeric extension, to apply it int main() { diff --git a/example/affine.md b/example/affine.md new file mode 100644 index 0000000000..9eeeb24a88 --- /dev/null +++ b/example/affine.md @@ -0,0 +1,19 @@ +# Affine transformation + +Affine transformation capabilities in GIL are demonstrated by the program `affine`, compiled from the sources `example/affine.cpp`. + +## Synopsis +`affine` + +The program doesn't take any argument on the command line. + +`affine` expects to find an image called `test.jpg` in the current directory, and produces an image in return, where the transformations have been applied: `out-affine.jpg` + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +- `affine` expects to find an image called `test.jpg` in the current directory. diff --git a/example/anisotropic_diffusion.cpp b/example/anisotropic_diffusion.cpp index 03defb7566..c96533647c 100644 --- a/example/anisotropic_diffusion.cpp +++ b/example/anisotropic_diffusion.cpp @@ -5,6 +5,7 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include #include @@ -20,6 +21,12 @@ namespace gil = boost::gil; +// Demonstrates Anisotropic Diffusion + +// This example uses the Perona-Malik's diffusion algorithm, which is the default in GIL. +// In addition, Gaussian conductivity and two wide range conductivity functions are also available. +// see include/boost/gil/image_processing/diffusion.hpp + void gray_version(std::string const& input_path, std::string const& output_path, unsigned int iteration_count, float kappa) { diff --git a/example/anisotropic_diffusion.md b/example/anisotropic_diffusion.md new file mode 100644 index 0000000000..36b942fae8 --- /dev/null +++ b/example/anisotropic_diffusion.md @@ -0,0 +1,22 @@ +# Anisotropic diffusion + +Anisotropic diffusion capabilities in GIL are demonstrated by the program `anisotropic_diffusion`, compiled from the sources `example/anisotropic_diffusion.cpp`. + +## Synopsis +`anisoptropic_diffusion input.png output.png gray|rgb iterations kappa` +- The first parameter must be the full path to an existing image in the JPEG format for `anisoptropic_diffusion` to process +- The second parameter is the full path to the output image of `anisotropic_diffusion`. The directory will *not* be created and must exist. +- The third parameter is the colour space: either `gray` or `rgb` +- The fourth parameter is the number of iterations, which *must* be a positive integer +- The fifth and last parameter is the value of the kappa constant + +Note that both the input and the ouput images must be in the PNG format. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +`anisotropic_diffusion` has no specific execution requirements. diff --git a/example/convolution.cpp b/example/convolution.cpp index 4844f35aac..b5e911da97 100644 --- a/example/convolution.cpp +++ b/example/convolution.cpp @@ -4,12 +4,24 @@ // Distributed under 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 -// Example for convolve_rows() and convolve_cols() in the numeric extension +// Convolves the image with a Gaussian kernel. + +// Note that the kernel can be fixed or resizable: +// kernel_1d_fixed k(matrix, centre) produces a fixed kernel +// kernel_1d k(matrix, size, centre) produces a resizable kernel + +// Work can be done row by row and column by column, as in this example, +// using the functions convolve_rows and convolve_cols (or their _fixed counterpart) +// but the header boost/gil/extension/numeric/convolve.hpp also offers the function convolve_1d which combines the two. + +// See also: +// convolve2d.cpp - Convolution with 2d kernels int main() { using namespace boost::gil; diff --git a/example/convolution.md b/example/convolution.md new file mode 100644 index 0000000000..70ba03ae59 --- /dev/null +++ b/example/convolution.md @@ -0,0 +1,19 @@ +# Convolution + +Convolution capabilities in GIL are demonstrated by the program `convolution`, compiled from the sources `example/convolution.cpp`. + +## Synopsis +`convolution` + +The program doesn't take any argument on the command line. + +`convolution` expects to find an image called `test.jpg` in the current directory, and produces two images in return, where the filters have been applied: `out-convolution.jpg` and `out-convolution2.jpg` + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +- `convolution` expects to find an image called `test.jpg` in the current directory. diff --git a/example/convolve2d.cpp b/example/convolve2d.cpp index 338f49a3ab..9d729b7366 100644 --- a/example/convolve2d.cpp +++ b/example/convolve2d.cpp @@ -1,3 +1,11 @@ +// +// Copyright 2019 Miral Shah +// +// Distributed under 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 @@ -5,14 +13,23 @@ #include #include + using namespace boost::gil; using namespace std; + +// Convolves the image with a 2d kernel. + +// Note that the kernel can be fixed or resizable: +// kernel_2d_fixed k(elements, centre_y, centre_x) produces a fixed kernel +// kernel_2d k(elements, size, centre_y, centre_x) produces a resizable kernel +// The size of the kernel matrix is deduced as the square root of the number of the elements (9 elements yield a 3x3 matrix) + +// See also: +// convolution.cpp - Convolution with 2d kernels + + int main() { - //gray8_image_t img; - //read_image("test_adaptive.png", img, png_tag{}); - //gray8_image_t img_out(img.dimensions()); - gray8_image_t img; read_image("src_view.png", img, png_tag{}); gray8_image_t img_out(img.dimensions()), img_out1(img.dimensions()); @@ -21,21 +38,16 @@ int main() detail::kernel_2d kernel(v.begin(), v.size(), 1, 1); detail::convolve_2d(view(img), kernel, view(img_out1)); - //write_view("out-convolve2d.png", view(img_out), png_tag{}); - write_view("out-convolve2d.png", view(img_out1), jpeg_tag{}); + write_view("out-convolve2d.png", view(img_out1), png_tag{}); - - //------------------------------------// std::vector v1(3, 1.0f / 3.0f); kernel_1d kernel1(v1.begin(), v1.size(), 1); detail::convolve_1d(const_view(img), kernel1, view(img_out), boundary_option::extend_zero); write_view("out-convolve_option_extend_zero.png", view(img_out), png_tag{}); - if (equal_pixels(view(img_out1), view(img_out)))cout << "convolve_option_extend_zero" << endl; - - cout << "done\n"; - cin.get(); + if (equal_pixels(view(img_out1), view(img_out))) + cout << "convolve_option_extend_zero" << endl; return 0; } diff --git a/example/convolve2d.md b/example/convolve2d.md new file mode 100644 index 0000000000..1027d4d390 --- /dev/null +++ b/example/convolve2d.md @@ -0,0 +1,22 @@ +# Convolution (2d kernel) + +2d kernel convolution capabilities in GIL are demonstrated by the program `convolve2d`, compiled from the sources `example/convolve2d.cpp`. + +## Synopsis +`convolve2d` + +The program doesn't take any argument on the command line. + +`convolve2d` expects to find an image called `src_view.png` in the current directory, and produces two images in return, where the filters have been applied: `out-convolve2d.png` and `out-convolve_option_extend_zero.png` + +Note that the user is expected to press a key to end the program. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured +- The PNG library installed and configured. + +### Execution requirements +- `convolve2d` expects to find an image called `src_view.png` in the current directory. diff --git a/example/dynamic_image.cpp b/example/dynamic_image.cpp index d7abdbf390..5226572414 100644 --- a/example/dynamic_image.cpp +++ b/example/dynamic_image.cpp @@ -5,18 +5,23 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #include #include #include #include +// Demonstrates how to use images whose type is instantiated at run time. + int main() { namespace gil = boost::gil; gil::any_image dynamic_image; gil::read_image("test.jpg", dynamic_image, gil::jpeg_tag()); + // Save the image upside down, preserving its native color space and channel depth auto view = gil::flipped_up_down_view(gil::const_view(dynamic_image)); + gil::write_view("out-dynamic_image.jpg", view, gil::jpeg_tag()); } diff --git a/example/dynamic_image.md b/example/dynamic_image.md new file mode 100644 index 0000000000..60ff20d172 --- /dev/null +++ b/example/dynamic_image.md @@ -0,0 +1,19 @@ +# Dynamic Image + +Dynamic image manipulation capabilities in GIL are demonstrated by the program `dynamic_image`, compiled from the sources `example/dynamic_image.cpp`. + +## Synopsis +`dynamic_image` + +The program doesn't take any argument on the command line. + +`dynamic_image` expects to find an image called `test.jpg` in the current directory, and produces an image in return, where the a flip has been applied: `out-dynamic_image.jpg`. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured + +### Execution requirements +- `dynamic_image` expects to find an image called `test.jpg` in the current directory. diff --git a/example/harris.cpp b/example/harris.cpp index fd9bede286..532c6e8d25 100644 --- a/example/harris.cpp +++ b/example/harris.cpp @@ -5,6 +5,7 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include #include @@ -23,6 +24,8 @@ namespace gil = boost::gil; +// Demonstrates Harris corner detection + // some images might produce artifacts // when converted to grayscale, // which was previously observed on @@ -157,11 +160,11 @@ int main(int argc, char* argv[]) gil::rgb8_image_t input_image; - gil::read_image(argv[1], input_image, gil::png_tag{}); + gil::read_image(argv[1], input_image, gil::png_tag{}); auto original_image = input_image; auto original_view = gil::view(original_image); auto input_view = gil::view(input_image); - auto grayscaled = to_grayscale(input_view); + auto grayscaled = to_grayscale(input_view); gil::gray8_image_t smoothed_image(grayscaled.dimensions()); auto smoothed = gil::view(smoothed_image); apply_gaussian_blur(gil::view(grayscaled), smoothed); diff --git a/example/harris.md b/example/harris.md new file mode 100644 index 0000000000..364abceed5 --- /dev/null +++ b/example/harris.md @@ -0,0 +1,21 @@ +# Harris Corner Detection + +Harris corner detection capabilities in GIL are demonstrated by the program `harris`, compiled from the sources `example/harris.cpp` and `hvstack.hpp`. + +## Synopsis +`harris input.png window-size discriminant harris-threshold output.png` + +- The first parameter must be the full path to an existing image in the PNG format for `harris` to process +- The second parameter is the size of the window containing the pixels to analyse and must be an odd number (e.g. 9 for a 3x3 matrix) +- The third parameter is the empirically-defined discriminant constant, usually between 0.04 and 0.06 +- The fourth parameter is the harris threshold used to identify the optimal values +- The fifth and last parameter is the full path to the output image of `harris`. The directory will *not* be created and must exist. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured + +### Execution requirements +- `harris` has no specific execution requirements. diff --git a/example/hessian.cpp b/example/hessian.cpp index cf05050bcc..50ac8b2e27 100644 --- a/example/hessian.cpp +++ b/example/hessian.cpp @@ -1,3 +1,11 @@ +// +// Copyright 2019 Olzhas Zhumabek +// +// Distributed under 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 @@ -11,6 +19,8 @@ namespace gil = boost::gil; +// Demonstrates Hessian feature (blob) detection + // some images might produce artifacts // when converted to grayscale, // which was previously observed on diff --git a/example/hessian.md b/example/hessian.md new file mode 100644 index 0000000000..b759712c9f --- /dev/null +++ b/example/hessian.md @@ -0,0 +1,20 @@ +# Hessian Feature Detection + +Hessian feature detection capabilities in GIL are demonstrated by the program `hessian`, compiled from the sources `example/hessian.cpp`. + +## Synopsis +`hessian input.png window-size hessian-threshold output.png` + +- The first parameter must be the full path to an existing image in the PNG format for `hessian` to process +- The second parameter is the size of the window containing the pixels to analyse and must be an odd number (e.g. 9 for a 3x3 matrix) +- The third parameter is the hessian threshold used to identify the optimal values +- The fourth and last parameter is the full path to the output image of `hessian`. The directory will *not* be created and must exist. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured + +### Execution requirements +- `hessian` has no specific execution requirements. diff --git a/example/histogram.cpp b/example/histogram.cpp index 229b8d9b69..8adc40e9ca 100644 --- a/example/histogram.cpp +++ b/example/histogram.cpp @@ -14,10 +14,13 @@ using namespace boost::gil; -/* -This file explains how to use the histogram class and some of its features -that can be applied for a variety of tasks. -*/ +// Explains how to use the histogram class and some of its features +// that can be applied for a variety of tasks. + +// See also: +// histogram_equalization.cpp - Regular Histogram Equalization +// adaptive_he.cpp - Adaptive Histogram Equalization +// histogram_matching.cpp - Reference-based histogram computation int main() { @@ -34,7 +37,7 @@ int main() h, // Histogram to be filled 1, // Histogram bin widths false, // Specify whether to accumulate over the values already present in h (default = false) - true, // Specify whether to have a sparse or continuous histogram (default = true) + true, // Specify whether to have a sparse (true) or continuous histogram (false) (default = true) false, // Specify if image mask is to be specified {{}}, // Mask as a 2D vector. Used only if prev argument specified {0}, // Lower limit on the values in histogram (default numeric_limit::min() on axes) diff --git a/example/histogram.md b/example/histogram.md new file mode 100644 index 0000000000..2a33f42856 --- /dev/null +++ b/example/histogram.md @@ -0,0 +1,20 @@ +# Histogram + +Histogram capabilities in GIL are demonstrated by the program `histogram`, compiled from the sources `example/histogram.cpp`. + +## Synopsis +`histogram` + +The program doesn't take any argument on the command line. + +`histogram` expects to find an image called `test_adaptive.png` in the current directory. +The program doesn't produce any output. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +- `histogram` expects to find an image called `test_adaptive.png` in the current directory. diff --git a/example/histogram_equalization.cpp b/example/histogram_equalization.cpp index e076f9f747..ae2ffd6329 100644 --- a/example/histogram_equalization.cpp +++ b/example/histogram_equalization.cpp @@ -1,9 +1,24 @@ +// +// Copyright 2020 Debabrata Mandal +// +// Distributed under 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 using namespace boost::gil; +// Demonstrates Histogram Equalization + +// See also: +// histogram.cpp - General use of histograms in GIL +// adaptive_he.cpp - Adaptive Histogram Equalization +// histogram_matching.cpp - Reference-based histogram computation + int main() { gray8_image_t img; diff --git a/example/histogram_equalization.md b/example/histogram_equalization.md new file mode 100644 index 0000000000..c3dcd57832 --- /dev/null +++ b/example/histogram_equalization.md @@ -0,0 +1,19 @@ +# Histogram Equalization + +Histogram equalization capabilities in GIL are demonstrated by the program `histogram_equalization`, compiled from the sources `example/histogram_equalization.cpp`. + +## Synopsis +`histogram_equalization` + +The program doesn't take any argument on the command line. + +`histogram_equalization` expects to find an image called `test_adaptive.png` in the current directory, and produces an image in return, where the equalization have been applied: `histogram_gray_equalized.png`. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +- `histogram_equalization` expects to find an image called `test_adaptive.png` in the current directory. diff --git a/example/histogram_matching.cpp b/example/histogram_matching.cpp index 494dcfd81b..1a060421c5 100644 --- a/example/histogram_matching.cpp +++ b/example/histogram_matching.cpp @@ -14,6 +14,13 @@ using namespace boost::gil; +// Demonstrates Histogram Matching + +// See also: +// histogram_equalization.cpp - Regular Histogram Equalization +// adaptive_he.cpp - Adaptive Histogram Equalization +// histogram.cpp - General use of histograms in GIL + std::vector> get_mask(gray8_view_t const& mask) { std::vector> mask_vec(mask.height(), std::vector(mask.width(), 0)); diff --git a/example/histogram_matching.md b/example/histogram_matching.md new file mode 100644 index 0000000000..47f16155dc --- /dev/null +++ b/example/histogram_matching.md @@ -0,0 +1,19 @@ +# Histogram Matching + +Histogram matching capabilities in GIL are demonstrated by the program `histogram_matching`, compiled from the sources `example/histogram_matching.cpp`. + +## Synopsis +`histogram_matching` + +The program doesn't take any argument on the command line. + +`histogram_matching` expects to find an image called `test_adaptive.png` in the current directory, and produces an image in return, where the equalization have been applied: `histogram_gray_matching.png`. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +- `histogram_matching` expects to find an image called `test_adaptive.png` in the current directory. diff --git a/example/hough_transform_circle.cpp b/example/hough_transform_circle.cpp index 9f83da9d0d..1e9d25798d 100644 --- a/example/hough_transform_circle.cpp +++ b/example/hough_transform_circle.cpp @@ -1,4 +1,3 @@ -// Boost.GIL (Generic Image Library) - tests // // Copyright 2020 Olzhas Zhumabek // @@ -6,6 +5,7 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include @@ -15,6 +15,16 @@ namespace gil = boost::gil; +// Demonstrates how to use a Hough transform to identify a circle + +// Note this relies on the brute force approach, which today is the only one available in GIL +// The function hough_circle_transform_brute, defined in include/boost/gil/image_processing/hough_transform.cpp, +// accepts a greyscale edge map, the three Hough parameters allowing to do the drawing and the voting, +// an accumulator in the form of an iterator of views of the parameter space and a utility rasterizer to produce the points. +// The example outputs the voting cell of the centre of a circle drawn programatically. +// See also: +// hough_transform_line.cpp - Hough transform to detect lines + int main() { const std::size_t size = 128; diff --git a/example/hough_transform_circle.md b/example/hough_transform_circle.md new file mode 100644 index 0000000000..253912584a --- /dev/null +++ b/example/hough_transform_circle.md @@ -0,0 +1,18 @@ +# Hough Circle Transform + +Hough circle transform capabilities in GIL are demonstrated by the program `hough_transform_circle`, compiled from the sources `example/hough_transform_circle.cpp`. + +## Synopsis +`hough_transform_circle` + +The program doesn't take any argument on the command line. + +The program outputs the voting cell of the centre of a circle drawn programatically. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above. + +### Execution requirements +`hough_transform_circle` doesn't have any specific execution requirements. diff --git a/example/hough_transform_line.cpp b/example/hough_transform_line.cpp index 29c8fc6def..75c9e611b6 100644 --- a/example/hough_transform_line.cpp +++ b/example/hough_transform_line.cpp @@ -1,4 +1,3 @@ -// Boost.GIL (Generic Image Library) - tests // // Copyright 2020 Olzhas Zhumabek // @@ -6,6 +5,7 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include #include @@ -14,6 +14,15 @@ namespace gil = boost::gil; +// Demonstrates the Hough transform to detect lines + +// The algorithm itself is implemented in include/boost/gil/image_processing/hough_transform.hpp. +// It follows the regular algorithm, using Hesse notation, and steps around each point using the minimal visible angle +// defined as atan2(1, d), where d is whichever dimension in the input image is the longest. +// The function make_theta_parameter, defined in include/boost/gil/image_processing/hough_parameter.hpp, allows to generate the parameter accordingly. +// See also: +// hough_transform_circle.cpp - Hough transform to detect circles + int main() { std::ptrdiff_t size = 32; @@ -38,11 +47,9 @@ int main() std::cout << '\n'; } - double minimum_theta_step = std::atan(1.0 / size); // this is the expected theta double _45_degrees = gil::detail::pi / 4; double _5_degrees = gil::detail::pi / 36; - std::size_t step_count = 5; auto theta_parameter = gil::make_theta_parameter(_45_degrees, _5_degrees, input_view.dimensions()); auto expected_radius = static_cast(std::round(std::cos(_45_degrees) * size)); diff --git a/example/hough_transform_line.md b/example/hough_transform_line.md new file mode 100644 index 0000000000..9fd0171b9f --- /dev/null +++ b/example/hough_transform_line.md @@ -0,0 +1,18 @@ +# Hough Line Transform + +Hough line transform capabilities in GIL are demonstrated by the program `hough_transform_line`, compiled from the sources `example/hough_transform_line.cpp`. + +## Synopsis +`hough_transform_line` + +The program doesn't take any argument on the command line. + +The program outputs the expected theta and radius, followed by every step of the search. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above. + +### Execution requirements +`hough_transform_line` doesn't have any specific execution requirements. diff --git a/example/hvstack.hpp b/example/hvstack.hpp index 8f120a10b6..b5bcc08bd8 100644 --- a/example/hvstack.hpp +++ b/example/hvstack.hpp @@ -1,3 +1,11 @@ +// +// // Copyright 2021 Olzhas Zhumabek +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// + #include "boost/gil/image_view_factory.hpp" #include #include diff --git a/example/interleaved_ptr.cpp b/example/interleaved_ptr.cpp index df80003b8c..1befd8cb0a 100644 --- a/example/interleaved_ptr.cpp +++ b/example/interleaved_ptr.cpp @@ -5,13 +5,17 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #ifdef WIN32 #define _CRT_SECURE_NO_DEPRECATE 1 #pragma warning(disable : 4244) // #pragma warning(disable : 4996) // MSFT declared it deprecated #endif -// Example file to demonstrate how to create a model of a pixel iterator +// Illustrates how to create a custom pixel reference and iterator. +// Creates a GIL image view over user-supplied data without the need to cast to GIL pixel type. +// The pixel iterator itself is implemented in interleaved_ptr.hpp, with the reference defined in interleaved_ref.hpp. + // FIXME: Review and remove if possible: gcc doesn't compile unless we forward-declare at_c before we include gil... namespace boost { namespace gil { diff --git a/example/interleaved_ptr.hpp b/example/interleaved_ptr.hpp index a60fc9d433..643af90099 100644 --- a/example/interleaved_ptr.hpp +++ b/example/interleaved_ptr.hpp @@ -5,6 +5,7 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #ifndef BOOST_GIL_EXAMPLE_INTERLEAVED_PTR_HPP #define BOOST_GIL_EXAMPLE_INTERLEAVED_PTR_HPP diff --git a/example/interleaved_ptr.md b/example/interleaved_ptr.md new file mode 100644 index 0000000000..0575492e94 --- /dev/null +++ b/example/interleaved_ptr.md @@ -0,0 +1,19 @@ +# Custom pixel reference and iteraror: Interleaved Pointer + +Definition of custom pixel reference and iterator capabilities in GIL are demonstrated by the program `interleaved_ptr`, compiled from the sources `example/interleaved_ptr.cpp`, `interleaved_ptr.hpp` and `interleaved_ref.hpp`. + +## Synopsis +`interleaved_ptr` + +The program doesn't take any argument on the command line. + +The program expects to find an image, `test.jpg` in the current directory, and produces the image `out-interleaved_ptr.jpg` in return, that has been generated using the custom pixel reference and iterator objects. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +`interleaved_ptr` expects to find an image, `test.jpg` in the current directory. diff --git a/example/interleaved_ref.hpp b/example/interleaved_ref.hpp index 837e7f222c..b016d36c13 100644 --- a/example/interleaved_ref.hpp +++ b/example/interleaved_ref.hpp @@ -5,6 +5,7 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #ifndef BOOST_GIL_EXAMPLE_INTERLEAVED_REF_HPP #define BOOST_GIL_EXAMPLE_INTERLEAVED_REF_HPP diff --git a/example/mandelbrot.cpp b/example/mandelbrot.cpp index 1aa5a90897..fd89975ec9 100644 --- a/example/mandelbrot.cpp +++ b/example/mandelbrot.cpp @@ -5,11 +5,16 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #include #include #include -// Example for convolve_rows() and convolve_cols() in the numeric extension +// Creates a synthetic image defining the Mandelbrot set. +// The example relies on a virtual_2d_locator to iterate over the pixels in the destination view. +// The pixels (of type rgb8_pixel_t) are generated programmatically, and the code shows how to access +// the colour channels and set them to arbitrary values. + using namespace boost::gil; diff --git a/example/mandelbrot.md b/example/mandelbrot.md new file mode 100644 index 0000000000..07d24b8e34 --- /dev/null +++ b/example/mandelbrot.md @@ -0,0 +1,19 @@ +# Mandelbrot + +Pixel iteration using `virtual_2d_locators` capabilities in GIL are demonstrated by the program `mandelbrot`, compiled from the sources `example/mandelbrot.cpp`. + +## Synopsis +`mandelbrot` + +The program doesn't take any argument on the command line. + +The program produces the image `out-mandelbrot.jpg` that has been generated using the `virtual_2d_locator` object. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +`mandelbrot` has no specific execution requirements. diff --git a/example/morphology.cpp b/example/morphology.cpp index e54bbfef6a..4e99d58709 100644 --- a/example/morphology.cpp +++ b/example/morphology.cpp @@ -5,6 +5,7 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include #include @@ -12,12 +13,26 @@ #include #include + +// Demonstrates a number of morphological operations +// The structuring element is defined as an instance of a kernel_2d: // Default structuring element is SE = [1,1,1] // |1,1,1| // [1,1,1] // SE(1,1)(center pixel) is the one which coincides with the currently // considered pixel of the image to be convolved. The structuring element can be // easily changed by the user. +// The example demonstrates the following morphological operations: +// - black_hat +// - top_hat +// - morphological_gradient +// - dilation +// - erosion +// - opening +// - closing +// - binary +// These operations are defined in include/boost/gil/image_processing/morphology.hpp + namespace gil = boost::gil; int main(int argc, char** argv) { diff --git a/example/morphology.md b/example/morphology.md new file mode 100644 index 0000000000..7ebb0f584d --- /dev/null +++ b/example/morphology.md @@ -0,0 +1,31 @@ +# Morphology + +Morphological operations capabilities in GIL are demonstrated by the program `morphology`, compiled from the sources `example/morphology.cpp`. + +## Synopsis +`morphology input.png output-image-template operation1 [operation2 ... operationN]` + +- The first parameter must be the full path to an existing image in the PNG format for `morphology` to process +- The second parameter is the pattern to use to name the output files. For example, a template of `out-` will generate files like `out-erosion.png`. Note that a full path can be given here, but that the directory must exist, as `morphology` will *not* create it. +- The rest of the parameters are operation names, separated by a space. Each operation triggers the output of a file, whose name follows the pattern: `_output-image-template_-_operation_-.png`. For example, the line `morphology input.png out- erosion` will produce the image `out-erosion.png`. + +The morphological operations available are the following: +- black_hat +- top_hat +- morphological_gradient +- dilation +- erosion +- opening +- closing +- binary + +The operations can be provided in any order, only note that if `binary` is supplied, it will be applied first. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +`morphology` has no specific execution requirements. diff --git a/example/packed_pixel.cpp b/example/packed_pixel.cpp index 573b668501..03aac5db8f 100644 --- a/example/packed_pixel.cpp +++ b/example/packed_pixel.cpp @@ -5,6 +5,7 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #include #include diff --git a/example/packed_pixel.md b/example/packed_pixel.md new file mode 100644 index 0000000000..3792ced288 --- /dev/null +++ b/example/packed_pixel.md @@ -0,0 +1,19 @@ +# Packed Pixel + +Packed pixel formats capabilities in GIL are demonstrated by the program `packed_pixel`, compiled from the sources `example/packed_pixel.cpp`. + +## Synopsis +`packed_pixel` + +The program doesn't take any argument on the command line. + +`packed_pixel` expects to find an image called `input.jpg` in the current directory in JPEG format, and produces two images in the current directory in return, `out-packed_pixel_bgr772.jpg` and `out-packed_pixel_gray1.jpg`. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +`packed_pixel` expects to find an image called `input.jpg` in the current directory in JPEG format. diff --git a/example/rasterizer_circle.cpp b/example/rasterizer_circle.cpp index 4d2997afb0..2c56de3199 100644 --- a/example/rasterizer_circle.cpp +++ b/example/rasterizer_circle.cpp @@ -1,4 +1,3 @@ -// Boost.GIL (Generic Image Library) - tests // // Copyright 2020 Olzhas Zhumabek // @@ -6,6 +5,7 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include #include @@ -14,6 +14,15 @@ namespace gil = boost::gil; +// Demonstrates the use of a rasterizer to generate an image of a circle +// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, +// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// This example uses a trigonometric rasterizer; GIL also offers the rasterizer midpoint_circle_rasterizer, +// which implements the Midpoint algorithm. +// See also: +// rasterizer_ellipse.cpp - Demonstrates the use of a rasterizer to generate an image of an ellipse +// rasterizer_line.cpp - Demonstrates the use of a rasterizer to generate an image of a line + int main() { const std::ptrdiff_t size = 256; diff --git a/example/rasterizer_circle.md b/example/rasterizer_circle.md new file mode 100644 index 0000000000..bf435d8c43 --- /dev/null +++ b/example/rasterizer_circle.md @@ -0,0 +1,19 @@ +# Circle rasterizeration + +Circle rasterization capabilities in GIL are demonstrated by the program `rasterizer_circle`, compiled from the sources `example/rasterizer_circle.cpp`. + +## Synopsis +`rasterizer_circle` + +The program doesn't take any argument on the command line. + +`rasterizer_circle` produces an image in the current directory called `circle.png`, in the PNG format. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +`rasterizer_circle` doesn't have any specific execution requirements. diff --git a/example/rasterizer_ellipse.cpp b/example/rasterizer_ellipse.cpp index 461103ac9e..1b31a5e5de 100644 --- a/example/rasterizer_ellipse.cpp +++ b/example/rasterizer_ellipse.cpp @@ -5,11 +5,23 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include namespace gil = boost::gil; +// Demonstrates the use of a rasterizer to generate an image of an ellipse +// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, +// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// The rasterizer used is a generalisation of the midpoint algorithm often used for drawing circle. +// This examples also shows how to create images with various pixel depth, as well as the behaviour +// in case of the rasterization of a curve that doesn't fit in a view. +// See also: +// rasterizer_circle.cpp - Demonstrates the use of a rasterizer to generate an image of a circle +// rasterizer_line.cpp - Demonstrates the use of a rasterizer to generate an image of a line + + int main() { // Syntax for usage :- diff --git a/example/rasterizer_ellipse.md b/example/rasterizer_ellipse.md new file mode 100644 index 0000000000..8851e30d99 --- /dev/null +++ b/example/rasterizer_ellipse.md @@ -0,0 +1,22 @@ +# Ellipse rasterizeration + +Ellipse rasterization capabilities in GIL are demonstrated by the program `rasterizer_ellipse`, compiled from the sources `example/rasterizer_ellipse.cpp`. + +## Synopsis +`rasterizer_ellipse` + +The program doesn't take any argument on the command line. + +`rasterizer_ellipse` produces three images in the current directory: +- `rasterized_ellipse_gray.jpg`, in the JPEG format, wich is a greyscale image of an ellipse +- `rasterized_ellipse_rgb.jpg`, in the JPEG format, wich is an RGB image of an ellipse +- `rasterized_ellipse_rgb_out_of_bound.jpg`, in the JPEG format, wich is an RGB image of an ellipse bigger than the containinig view. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +`rasterizer_ellipse` doesn't have any specific execution requirements. diff --git a/example/rasterizer_line.cpp b/example/rasterizer_line.cpp index 9f02a347a4..b1c3526f74 100644 --- a/example/rasterizer_line.cpp +++ b/example/rasterizer_line.cpp @@ -1,4 +1,3 @@ -// Boost.GIL (Generic Image Library) - tests // // Copyright 2020 Olzhas Zhumabek // @@ -6,6 +5,7 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // + #include #include @@ -14,6 +14,16 @@ namespace gil = boost::gil; +// Demonstrates the use of a rasterizer to generate an image of a line +// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, +// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// The rasterizer used implements the Bresenham's line algorithm. +// Multiple images are created, all of the same size, but with areas of different sizes passed to the rasterizer, resulting in different lines. +// See also: +// rasterizer_circle.cpp - Demonstrates the use of a rasterizer to generate an image of a circle +// rasterizer_ellipse.cpp - Demonstrates the use of a rasterizer to generate an image of an ellipse + + const std::ptrdiff_t size = 256; void line_bresenham(std::ptrdiff_t width, std::ptrdiff_t height, const std::string& output_name) diff --git a/example/rasterizer_line.md b/example/rasterizer_line.md new file mode 100644 index 0000000000..5ff366b305 --- /dev/null +++ b/example/rasterizer_line.md @@ -0,0 +1,23 @@ +# Line rasterizeration + +Line rasterization capabilities in GIL are demonstrated by the program `rasterizer_line`, compiled from the sources `example/rasterizer_line.cpp`. + +## Synopsis +`rasterizer_line` + +The program doesn't take any argument on the command line. + +`rasterizer_line` produces four images of different lines in the current directory: +- `line-bresenham-256-256.png`, in the PNG format +- `line-bresenham-256-128.png`, in the PNG format +- `line-bresenham-256-1.png`, in the PNG format +- `line-bresenham-1-256.png`, in the PNG format. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +`rasterizer_line` doesn't have any specific execution requirements. diff --git a/example/resize.cpp b/example/resize.cpp index 2dc0bf1993..663a3a6c64 100644 --- a/example/resize.cpp +++ b/example/resize.cpp @@ -5,12 +5,16 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #include #include #include #include -// Example for resize_view() in the numeric extension +// Demonstrates how to scale an image using bilinear sampling +// This example relies on the function resize_view(), available in include/boost/gil/extension/numeric/resample.hpp, +// to apply the resampling. +// This example demonstrates bilinear sampling; include/boost/gil/extension/numeric/sample.hpp also offers nearest-neighbour sampling. int main() { diff --git a/example/resize.md b/example/resize.md new file mode 100644 index 0000000000..7d5c5a1781 --- /dev/null +++ b/example/resize.md @@ -0,0 +1,19 @@ +# Resize + +Resizing capabilities in GIL are demonstrated by the program `resize`, compiled from the sources `example/resize.cpp`. + +## Synopsis +`resize` + +This program expects to find an image called `test.jpg`, in JPEG format, in the current directory, and produces a scaled down version of this image, called `out-resize.jpg`, in JPEG format in the current directory. + +The program doesn't take any argument on the command line. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +`resize` expects to find an image called `test.jpg`, in JPEG format, in the current directory. diff --git a/example/sobel_scharr.cpp b/example/sobel_scharr.cpp index 10e15c2230..b37694402f 100644 --- a/example/sobel_scharr.cpp +++ b/example/sobel_scharr.cpp @@ -1,3 +1,11 @@ +// +// // Copyright 2019 Olzhas Zhumabek +// +// Distributed under 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 @@ -7,6 +15,11 @@ namespace gil = boost::gil; +// Demonstrates the use of Sobel and Scharr filters to detect edges +// The example generates a couple of images representing respectively the result of +// applying the filter horizontally and vertically. +// The functions generating the filters (or kernels) are defined in include/boost/gil/image_processing/numeric.hpp. + int main(int argc, char* argv[]) { if (argc != 5) diff --git a/example/sobel_scharr.md b/example/sobel_scharr.md new file mode 100644 index 0000000000..73510f2fc5 --- /dev/null +++ b/example/sobel_scharr.md @@ -0,0 +1,20 @@ +# Sobel and Scharr filters for edge detection + +Edge detection via Sobel and Scharr filters capabilities in GIL are demonstrated by the program `sobel_scharr`, compiled from the sources `example/sobel_scharr.cpp`. + +## Synopsis +`sobel_scharr input.png sobel|scharr output-x.png output-y.png` + +- The first argument must be the full path to an existing image in PNG format +- The second argument is the type of the filter to apply, either `sobel` or `scharr` +- The third argument is the full path to an image of the result of applying the filter in the x direction. The directory will *not* be created and must exist +- The fourth and last argument is the full path to an image of the result of applying the filter in the y direction. The directory will *not* be created and must exist. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +`sobek_scharr` doesn't have any specific execution requirements. diff --git a/example/threshold.cpp b/example/threshold.cpp index 565ff1ca10..b71f071ba6 100644 --- a/example/threshold.cpp +++ b/example/threshold.cpp @@ -5,11 +5,26 @@ // 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; +// Demonstrates thresholding +// Thresholding can either attribute an arbitrary value to pixels whose values are greater than the threshold +// or can truncate the pixel values at an arbitrary maximum. +// In particular, the function threshold_truncate accepts a mode and a direction. +// Passing threshold_truncate_mode::threshold effectively truncates the values to the threshold, whereas +// threshold_truncate_mode::zero sets them to 0. +// The combination of mode and direction controls which pixels are modified: +// - threshold and regular: truncates the pixels whose values are greater than the threshold +// - threshold and inverse: truncates the pixels whose values are less than the threshold +// - zero and regular: zeroes the pixels whose values are less than the threshold +// - zero and inverse: zeroes the pixels whose values are greater than the threshold +// See also: +// adaptive_threshold.cpp - Adaptive thresholding + int main() { rgb8_image_t img; diff --git a/example/threshold.md b/example/threshold.md new file mode 100644 index 0000000000..79289637ef --- /dev/null +++ b/example/threshold.md @@ -0,0 +1,19 @@ +# Thresholding + +Thresholding capabilities in GIL are demonstrated by the program `threshold`, compiled from the sources `example/threshold.cpp`. + +## Synopsis +`threshold` + +The program expects to find an image in the JPEG format in the current directory named `input.jpg`, and produces two images in JPEG format in the current directory in return, named `out-threshold-binary.jpg` and `out-threshold-binary-inv.jpg`, where a thresholding and an inverse thresholding, respectively, has been applied. + +The program doesn't take any command line arguments. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +`threshold` expects to find an image in the JPEG format in the current directory. diff --git a/example/tutorial_histogram.cpp b/example/tutorial_histogram.cpp index b1b4896429..4986a9705a 100644 --- a/example/tutorial_histogram.cpp +++ b/example/tutorial_histogram.cpp @@ -5,13 +5,15 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #include #include #include #include -// Example file to demonstrate a way to compute histogram +// Demonstrates a way to compute a histogram +// This example relies on a colour-converting view to work with a greyscale view using namespace boost::gil; diff --git a/example/tutorial_histogram.md b/example/tutorial_histogram.md new file mode 100644 index 0000000000..bb41d7ebe0 --- /dev/null +++ b/example/tutorial_histogram.md @@ -0,0 +1,19 @@ +# Histogram computation + +Histogram computation capabilities in GIL are demonstrated by the program `tutorial_histogram`, compiled from the sources `example/tutorial_histogram.cpp`. + +## Synopsis +`tutorial_histogram` + +The program expects to find an image in the JPEG format in the current directory named `input.jpg`, and produces a text file named `out-histogram.txt`, in the current direction, where the histogram has been calculated.. + +The program doesn't take any command line arguments. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +`tutorial_histogram` expects to find an image in the JPEG format in the current directory. diff --git a/example/x_gradient.cpp b/example/x_gradient.cpp index 111d40b9e6..8d24d3a11b 100644 --- a/example/x_gradient.cpp +++ b/example/x_gradient.cpp @@ -5,9 +5,18 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // + #include -// Example to demonstrate a way to compute gradients along x-axis +// Demonstrates how to compute gradients along the x-axis +// This example converts the input image to a greyscale view via color_converted_view, +// and then relies on the function static_transform to apply the operation halfdiff_cast_channels. +// The result is captured in a view, initially blacked out via a call to fill_pixels (defined in +// include/boost/gil/algorithm.hpp) +// static_transform is defined in include/boost/gil/color_based_algorithm.hpp and applies an operation +// to either a single source or two sources and a destination (as is the case here). +// In this example, the gradient is calculated as half the difference between the two pixels surrounding x +// in the loop in x_gradient. using namespace boost::gil; diff --git a/example/x_gradient.md b/example/x_gradient.md new file mode 100644 index 0000000000..ab273dea22 --- /dev/null +++ b/example/x_gradient.md @@ -0,0 +1,19 @@ +# Gradients calculations + +Gradients calculations capabilities in GIL are demonstrated by the program `x_gradient`, compiled from the sources `example/x_gradient.cpp`. + +## Synopsis +`x_gradient` + +The program expects to find an image in the JPEG format in the current directory named `input.jpg`, and produces an image in JPEG format in the current directory in return, named `out-x_gradient.jpg`, where the gradients have been captured. + +The program doesn't take any command line arguments. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The JPEG library installed and configured. + +### Execution requirements +`x_gradient` expects to find an image in the JPEG format in the current directory. diff --git a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp index b1c189d948..9b2fe907a0 100644 --- a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp +++ b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp @@ -166,9 +166,7 @@ void non_overlapping_interpolated_clahe( std::vector sample_x; coord_t sample_x1 = tile_width_x / 2; - coord_t sample_x2 = (tile_width_x + 1) / 2; coord_t sample_y1 = tile_width_y / 2; - coord_t sample_y2 = (tile_width_y + 1) / 2; auto extend_left = tile_width_x; auto extend_top = tile_width_y; diff --git a/include/boost/gil/image_processing/histogram_matching.hpp b/include/boost/gil/image_processing/histogram_matching.hpp index 7019e278c6..4ab7f72abd 100644 --- a/include/boost/gil/image_processing/histogram_matching.hpp +++ b/include/boost/gil/image_processing/histogram_matching.hpp @@ -169,8 +169,6 @@ void histogram_matching( source_channel_t src_pixel_max = std::numeric_limits::max(); ref_channel_t ref_pixel_min = std::numeric_limits::min(); ref_channel_t ref_pixel_max = std::numeric_limits::max(); - dst_channel_t dst_pixel_min = std::numeric_limits::min(); - dst_channel_t dst_pixel_max = std::numeric_limits::max(); for (std::size_t i = 0; i < channels; i++) { diff --git a/include/boost/gil/image_processing/hough_transform.hpp b/include/boost/gil/image_processing/hough_transform.hpp index 6fd7148886..982c28c1f9 100644 --- a/include/boost/gil/image_processing/hough_transform.hpp +++ b/include/boost/gil/image_processing/hough_transform.hpp @@ -87,8 +87,6 @@ void hough_circle_transform_brute(const ImageView& input, const hough_parameter& y_parameter, ForwardIterator d_first, Rasterizer rasterizer) { - const auto width = input.width(); - const auto height = input.height(); for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; ++radius_index) { const auto radius = radius_parameter.start_point + From 4531c3d563cfade3eafb80a09f304747845bbe8b Mon Sep 17 00:00:00 2001 From: Dhruva Gole <41233856+DhruvaG2000@users.noreply.github.com> Date: Thu, 16 Dec 2021 15:04:55 +0530 Subject: [PATCH 069/193] docs: Fix TOC link to B2 in CONTRIBUTING.md (#632) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7520b87a9b..403ac05ef9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ please follow the workflow explained in this document. - [5. Update your pull request](#5-update-your-pull-request) - [Development](#development) - [Install dependencies](#install-dependencies) - - [Using B2](#using-boostbuild) + - [Using B2](#using-b2) - [Using CMake](#using-cmake) - [Running clang-tidy](#running-clang-tidy) - [Guidelines](#guidelines) From 2db5fe9f448bbf4b9da0e38414a366ec5188c817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 5 Feb 2022 14:13:11 +0100 Subject: [PATCH 070/193] =?UTF-8?q?Fix=20warning:=20unused=20variable=20?= =?UTF-8?q?=E2=80=98r=5Fsquared=E2=80=99=20[-Wunused-variable]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/core/rasterization/circle.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/rasterization/circle.cpp b/test/core/rasterization/circle.cpp index 68a91f4fd3..b4c85c7da4 100644 --- a/test/core/rasterization/circle.cpp +++ b/test/core/rasterization/circle.cpp @@ -19,7 +19,7 @@ void test_rasterizer_follows_equation(std::ptrdiff_t radius, Rasterizer rasteriz { std::vector circle_points(rasterizer.point_count(radius)); - std::ptrdiff_t r_squared = radius * radius; + std::ptrdiff_t const r_squared = radius * radius; rasterizer(radius, {0, 0}, circle_points.begin()); std::vector first_octant(rasterizer.point_count(radius) / 8); @@ -30,7 +30,7 @@ void test_rasterizer_follows_equation(std::ptrdiff_t radius, Rasterizer rasteriz for (const auto& point : first_octant) { - double y_exact = std::sqrt(radius * radius - point.x * point.x); + double y_exact = std::sqrt(r_squared - point.x * point.x); std::ptrdiff_t lower_result = static_cast(std::floor(y_exact)); std::ptrdiff_t upper_result = static_cast(std::ceil(y_exact)); BOOST_TEST(point.y >= lower_result && point.y <= upper_result); From 0accacdbe679766f69caebecbd0f40edb607fac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 5 Feb 2022 14:19:50 +0100 Subject: [PATCH 071/193] Fix warning: comparison of integer expressions of different signedness --- example/histogram_matching.cpp | 4 ++-- .../gil/image_processing/adaptive_histogram_equalization.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example/histogram_matching.cpp b/example/histogram_matching.cpp index 1a060421c5..308b5fc1d6 100644 --- a/example/histogram_matching.cpp +++ b/example/histogram_matching.cpp @@ -24,9 +24,9 @@ using namespace boost::gil; std::vector> get_mask(gray8_view_t const& mask) { std::vector> mask_vec(mask.height(), std::vector(mask.width(), 0)); - for (std::size_t i = 0; i < mask.height(); i++) + for (std::ptrdiff_t i = 0; i < mask.height(); i++) { - for (std::size_t j = 0; j < mask.width(); j++) + for (std::ptrdiff_t j = 0; j < mask.width(); j++) { mask_vec[i][j] = mask(j, i); } diff --git a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp index 9b2fe907a0..3ba0e6f55a 100644 --- a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp +++ b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp @@ -138,8 +138,8 @@ template void non_overlapping_interpolated_clahe( SrcView const& src_view, DstView const& dst_view, - std::size_t tile_width_x = 20, - std::size_t tile_width_y = 20, + std::ptrdiff_t tile_width_x = 20, + std::ptrdiff_t tile_width_y = 20, double clip_limit = 0.03, std::size_t bin_width = 1.0, bool mask = false, From 5dcdf53c02f6b646703b03670c66596beae905ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sun, 6 Feb 2022 19:11:25 +0100 Subject: [PATCH 072/193] Fix warning: unused variable in diffusion.hpp (#635) --- include/boost/gil/image_processing/diffusion.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/boost/gil/image_processing/diffusion.hpp b/include/boost/gil/image_processing/diffusion.hpp index eedd50ebb8..2a2edb097d 100644 --- a/include/boost/gil/image_processing/diffusion.hpp +++ b/include/boost/gil/image_processing/diffusion.hpp @@ -157,8 +157,6 @@ struct stencil_5points template Pixel reduce(const stencil_type& stencil) { - auto first = stencil.begin(); - auto last = stencil.end(); using channel_type = typename channel_type::type; auto result = []() { Pixel zero_pixel; From c7aba23c748456f2bb679be0d6011f3db2eb94fa Mon Sep 17 00:00:00 2001 From: Harshit Pant <70189550+harshitpant1@users.noreply.github.com> Date: Wed, 9 Feb 2022 19:13:48 +0530 Subject: [PATCH 073/193] Fix convolve_2d for images with float32_t channel model (#577) Fixes #575 --- .../boost/gil/extension/numeric/convolve.hpp | 2 +- test/extension/numeric/convolve_2d.cpp | 41 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/include/boost/gil/extension/numeric/convolve.hpp b/include/boost/gil/extension/numeric/convolve.hpp index de5732630f..29e7ad1132 100644 --- a/include/boost/gil/extension/numeric/convolve.hpp +++ b/include/boost/gil/extension/numeric/convolve.hpp @@ -399,7 +399,7 @@ void convolve_2d_impl(SrcView const& src_view, DstView const& dst_view, Kernel c col_boundary >= 0 && col_boundary < src_view.width()) { aux_total += - src_view(col_boundary, row_boundary) * + src_view(col_boundary, row_boundary)[0] * kernel.at(flip_ker_row, flip_ker_col); } } diff --git a/test/extension/numeric/convolve_2d.cpp b/test/extension/numeric/convolve_2d.cpp index fbfce5f96c..87efc7f6c2 100644 --- a/test/extension/numeric/convolve_2d.cpp +++ b/test/extension/numeric/convolve_2d.cpp @@ -60,9 +60,48 @@ void test_convolve_2d_with_normalized_mean_filter() BOOST_TEST(gil::equal_pixels(out_view, dst_view)); } +void test_convolve_2d_with_image_using_float32_t() +{ +gil::float32_t img[] = +{ + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.76, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.6, 0.6, 0.1, 0.54, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.84, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.1, 0.1, 0.1, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.3, 0.1, 0.1, 0.4, 0.1, 0.32, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.21, 0.1, 0.1 +}; + +gil::float32_t exp_output[] = +{ + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.76, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.6, 0.6, 0.1, 0.54, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.84, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.1, 0.1, 0.1, 0.4, 0.1, 0.5, 0.1, 0.1, + 0.1, 0.3, 0.1, 0.1, 0.4, 0.1, 0.32, 0.1, 0.1, + 0.1, 0.2, 0.1, 0.1, 0.4, 0.1, 0.21, 0.1, 0.1 +}; + +gil::gray32f_image_t img_gray_out(9, 9); +gil::gray32fc_view_t src_view = + gil::interleaved_view(9, 9, reinterpret_cast(img), 9); +gil::gray32fc_view_t exp_out_view = + gil::interleaved_view(9, 9, reinterpret_cast(exp_output), 9); +std::vector v(1, 1); +gil::detail::kernel_2d kernel(v.begin(), v.size(), 0, 0); // impulse kernel +gil::detail::convolve_2d(src_view, kernel, view(img_gray_out)); +BOOST_TEST(gil::equal_pixels(exp_out_view, view(img_gray_out))); +} + int main() { test_convolve_2d_with_normalized_mean_filter(); - + test_convolve_2d_with_image_using_float32_t(); return ::boost::report_errors(); } From 199520179e1b8e8324cc130145ed7879d5190c10 Mon Sep 17 00:00:00 2001 From: Pranam Lashkari Date: Wed, 9 Feb 2022 19:47:47 +0530 Subject: [PATCH 074/193] Refactor CMake test config (#634) --- test/core/image_processing/CMakeLists.txt | 43 +++++++---------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index 1581fd3075..22d503eb2f 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -10,7 +10,18 @@ foreach(_name threshold_binary threshold_truncate threshold_otsu - morphology) + morphology + lanczos_scaling + simple_kernels + harris + hessian + box_filter + median_filter + sobel_scharr + anisotropic_diffusion + hough_parameter + hough_line_transform + hough_circle_transform) set(_test t_core_image_processing_${_name}) set(_target test_core_image_processing_${_name}) @@ -26,33 +37,5 @@ foreach(_name unset(_name) unset(_target) -endforeach() - -foreach(_name - lanczos_scaling - simple_kernels - harris - hessian - box_filter - median_filter - sobel_scharr - anisotropic_diffusion - hough_parameter - hough_line_transform - hough_circle_transform) - set(_test t_core_image_processing_${_name}) - set(_target test_core_image_processing_${_name}) - - add_executable(${_target} "") - target_sources(${_target} PRIVATE ${_name}.cpp) - target_link_libraries(${_target} - PRIVATE - gil_compile_options - gil_include_directories - gil_dependencies) - add_test(NAME ${_test} COMMAND ${_target}) - - unset(_name) - unset(_target) - unset(_test) + unset(_test) endforeach() From 2562e112419226301effa78e77c55107b7a17bf4 Mon Sep 17 00:00:00 2001 From: Pranam Lashkari Date: Mon, 1 Mar 2021 16:47:45 +0530 Subject: [PATCH 075/193] numeric extension move into core 1 moved numeric/algorithm.hpp into core --- ...pp => adaptive_histogram_equalization.cpp} | 3 +- include/boost/gil/algorithm.hpp | 424 +++++++++++++++++ .../boost/gil/extension/numeric/algorithm.hpp | 432 +----------------- .../boost/gil/extension/numeric/convolve.hpp | 2 +- include/boost/gil/image.hpp | 5 +- include/boost/gil/image_processing/filter.hpp | 3 +- include/boost/gil/metafunctions.hpp | 3 +- test/core/algorithm/CMakeLists.txt | 4 +- test/core/algorithm/Jamfile | 2 + .../algorithm}/extend_boundary.cpp | 5 +- test/extension/numeric/CMakeLists.txt | 2 +- test/extension/numeric/Jamfile | 2 +- 12 files changed, 447 insertions(+), 440 deletions(-) rename example/{adaptive_he.cpp => adaptive_histogram_equalization.cpp} (91%) rename test/{extension/numeric => core/algorithm}/extend_boundary.cpp (98%) diff --git a/example/adaptive_he.cpp b/example/adaptive_histogram_equalization.cpp similarity index 91% rename from example/adaptive_he.cpp rename to example/adaptive_histogram_equalization.cpp index 3a0e23edc6..290e418fa0 100644 --- a/example/adaptive_he.cpp +++ b/example/adaptive_histogram_equalization.cpp @@ -1,5 +1,6 @@ // // Copyright 2020 Debabrata Mandal +// Copyright 2021 Pranam Lashkari // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -8,7 +9,7 @@ #include #include -#include +#include #include using namespace boost::gil; diff --git a/include/boost/gil/algorithm.hpp b/include/boost/gil/algorithm.hpp index cbd1716868..9b51c9c89c 100644 --- a/include/boost/gil/algorithm.hpp +++ b/include/boost/gil/algorithm.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -8,6 +9,11 @@ #ifndef BOOST_GIL_ALGORITHM_HPP #define BOOST_GIL_ALGORITHM_HPP +#include + +#include +#include +#include #include #include #include @@ -26,6 +32,7 @@ #include #include #include +#include namespace boost { namespace gil { @@ -1174,6 +1181,423 @@ F transform_pixel_positions(const View1& src1,const View2& src2,const View3& dst } return fun; } + + +// Code below this line is moved here from + +/// \brief Reference proxy associated with a type that has a \p "reference" member type alias. +/// +/// The reference proxy is the reference type, but with stripped-out C++ reference. +/// Models PixelConcept. +template +struct pixel_proxy : std::remove_reference {}; + +/// \brief std::for_each for a pair of iterators +template +BinaryFunction for_each(Iterator1 first1, Iterator1 last1, Iterator2 first2, BinaryFunction f) +{ + while (first1 != last1) + f(*first1++, *first2++); + return f; +} + +template +inline +auto assign_pixels(SrcIterator src, SrcIterator src_end, DstIterator dst) -> DstIterator +{ + for_each(src, src_end, dst, + pixel_assigns_t + < + typename pixel_proxy::value_type>::type, + typename pixel_proxy::value_type>::type + >()); + return dst + (src_end - src); +} + +namespace detail { + +template +struct inner_product_k_t +{ + template + < + class InputIterator1, + class InputIterator2, + class T, + class BinaryOperation1, + class BinaryOperation2 + > + static T apply( + InputIterator1 first1, + InputIterator2 first2, T init, + BinaryOperation1 binary_op1, + BinaryOperation2 binary_op2) + { + init = binary_op1(init, binary_op2(*first1, *first2)); + return inner_product_k_t::template apply( + first1 + 1, first2 + 1, init, binary_op1, binary_op2); + } +}; + +template <> +struct inner_product_k_t<0> +{ + template + < + class InputIterator1, + class InputIterator2, + class T, + class BinaryOperation1, + class BinaryOperation2 + > + static T apply( + InputIterator1 first1, + InputIterator2 first2, + T init, + BinaryOperation1 binary_op1, + BinaryOperation2 binary_op2) + { + return init; + } +}; + +} // namespace detail + +/// static version of std::inner_product +template +< + std::size_t Size, + class InputIterator1, + class InputIterator2, + class T, + class BinaryOperation1, + class BinaryOperation2 +> +BOOST_FORCEINLINE +T inner_product_k( + InputIterator1 first1, + InputIterator2 first2, + T init, + BinaryOperation1 binary_op1, + BinaryOperation2 binary_op2) +{ + return detail::inner_product_k_t::template apply( + first1, first2, init, binary_op1, binary_op2); +} + +/// \brief 1D un-guarded cross-correlation with a variable-size kernel +template +< + typename PixelAccum, + typename SrcIterator, + typename KernelIterator, + typename Size, + typename DstIterator +> +inline +auto correlate_pixels_n( + SrcIterator src_begin, + SrcIterator src_end, + KernelIterator kernel_begin, + Size kernel_size, + DstIterator dst_begin) + -> DstIterator +{ + using src_pixel_ref_t = typename pixel_proxy + < + typename std::iterator_traits::value_type + >::type; + using dst_pixel_ref_t = typename pixel_proxy + < + typename std::iterator_traits::value_type + >::type; + using kernel_value_t = typename std::iterator_traits::value_type; + + PixelAccum accum_zero; + pixel_zeros_t()(accum_zero); + while (src_begin != src_end) + { + pixel_assigns_t()( + std::inner_product( + src_begin, + src_begin + kernel_size, + kernel_begin, + accum_zero, + pixel_plus_t(), + pixel_multiplies_scalar_t()), + *dst_begin); + + ++src_begin; + ++dst_begin; + } + return dst_begin; +} + +/// \brief 1D un-guarded cross-correlation with a fixed-size kernel +template +< + std::size_t Size, + typename PixelAccum, + typename SrcIterator, + typename KernelIterator, + typename DstIterator +> +inline +auto correlate_pixels_k( + SrcIterator src_begin, + SrcIterator src_end, + KernelIterator kernel_begin, + DstIterator dst_begin) + -> DstIterator +{ + using src_pixel_ref_t = typename pixel_proxy + < + typename std::iterator_traits::value_type + >::type; + using dst_pixel_ref_t = typename pixel_proxy + < + typename std::iterator_traits::value_type + >::type; + using kernel_type = typename std::iterator_traits::value_type; + + PixelAccum accum_zero; + pixel_zeros_t()(accum_zero); + while (src_begin != src_end) + { + pixel_assigns_t()( + inner_product_k( + src_begin, + kernel_begin, + accum_zero, + pixel_plus_t(), + pixel_multiplies_scalar_t()), + *dst_begin); + + ++src_begin; + ++dst_begin; + } + return dst_begin; +} + +/// \brief destination is set to be product of the source and a scalar +/// \tparam PixelAccum - TODO +/// \tparam SrcView Models ImageViewConcept +/// \tparam DstView Models MutableImageViewConcept +template +inline +void view_multiplies_scalar(SrcView const& src_view, Scalar const& scalar, DstView const& dst_view) +{ + static_assert(std::is_scalar::value, "Scalar is not scalar"); + BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + using src_pixel_ref_t = typename pixel_proxy::type; + using dst_pixel_ref_t = typename pixel_proxy::type; + using y_coord_t = typename SrcView::y_coord_t; + + y_coord_t const height = src_view.height(); + for (y_coord_t y = 0; y < height; ++y) + { + typename SrcView::x_iterator it_src = src_view.row_begin(y); + typename DstView::x_iterator it_dst = dst_view.row_begin(y); + typename SrcView::x_iterator it_src_end = src_view.row_end(y); + while (it_src != it_src_end) + { + pixel_assigns_t()( + pixel_multiplies_scalar_t()(*it_src, scalar), + *it_dst); + + ++it_src; + ++it_dst; + } + } +} + + +/// \ingroup ImageAlgorithms +/// \brief Boundary options for image boundary extension +enum class boundary_option +{ + output_ignore, /// do nothing to the output + output_zero, /// set the output to zero + extend_padded, /// assume the source boundaries to be padded already + extend_zero, /// assume the source boundaries to be zero + extend_constant /// assume the source boundaries to be the boundary value +}; + +namespace detail +{ + +template +void extend_row_impl( + SrcView const& src_view, + RltView result_view, + std::size_t extend_count, + boundary_option option) +{ + std::ptrdiff_t extend_count_ = static_cast(extend_count); + + if (option == boundary_option::extend_constant) + { + for (std::ptrdiff_t i = 0; i < result_view.height(); i++) + { + if(i >= extend_count_ && i < extend_count_ + src_view.height()) + { + assign_pixels( + src_view.row_begin(i - extend_count_), + src_view.row_end(i - extend_count_), + result_view.row_begin(i) + ); + } + else if(i < extend_count_) + { + assign_pixels(src_view.row_begin(0), src_view.row_end(0), result_view.row_begin(i)); + } + else + { + assign_pixels( + src_view.row_begin(src_view.height() - 1), + src_view.row_end(src_view.height() - 1), + result_view.row_begin(i) + ); + } + + } + } + else if (option == boundary_option::extend_zero) + { + typename SrcView::value_type acc_zero; + pixel_zeros_t()(acc_zero); + + for (std::ptrdiff_t i = 0; i < result_view.height(); i++) + { + if (i >= extend_count_ && i < extend_count_ + src_view.height()) + { + assign_pixels( + src_view.row_begin(i - extend_count_), + src_view.row_end(i - extend_count_), + result_view.row_begin(i) + ); + } + else + { + std::fill_n(result_view.row_begin(i), result_view.width(), acc_zero); + } + } + } + else if (option == boundary_option::extend_padded) + { + auto original_view = subimage_view( + src_view, + 0, + -extend_count, + src_view.width(), + src_view.height() + (2 * extend_count) + ); + for (std::ptrdiff_t i = 0; i < result_view.height(); i++) + { + assign_pixels( + original_view.row_begin(i), + original_view.row_end(i), + result_view.row_begin(i) + ); + } + } + else + { + BOOST_ASSERT_MSG(false, "Invalid boundary option"); + } +} + +} //namespace detail + + +/// \brief adds new row at top and bottom. +/// Image padding introduces new pixels around the edges of an image. +/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques. +/// \tparam SrcView Models ImageViewConcept +/// \tparam extend_count number of rows to be added each side +/// \tparam option - TODO +template +auto extend_row( + SrcView const& src_view, + std::size_t extend_count, + boundary_option option +) -> typename gil::image +{ + typename gil::image + result_img(src_view.width(), src_view.height() + (2 * extend_count)); + + auto result_view = view(result_img); + detail::extend_row_impl(src_view, result_view, extend_count, option); + return result_img; +} + + +/// \brief adds new column at left and right. +/// Image padding introduces new pixels around the edges of an image. +/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques. +/// \tparam SrcView Models ImageViewConcept +/// \tparam extend_count number of columns to be added each side +/// \tparam option - TODO +template +auto extend_col( + SrcView const& src_view, + std::size_t extend_count, + boundary_option option +) -> typename gil::image +{ + auto src_view_rotate = rotated90cw_view(src_view); + + typename gil::image + result_img(src_view.width() + (2 * extend_count), src_view.height()); + + auto result_view = rotated90cw_view(view(result_img)); + detail::extend_row_impl(src_view_rotate, result_view, extend_count, option); + return result_img; +} + +/// \brief adds new row and column at all sides. +/// Image padding introduces new pixels around the edges of an image. +/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques. +/// \tparam SrcView Models ImageViewConcept +/// \tparam extend_count number of rows/column to be added each side +/// \tparam option - TODO +template +auto extend_boundary( + SrcView const& src_view, + std::size_t extend_count, + boundary_option option +) -> typename gil::image +{ + if (option == boundary_option::extend_padded) + { + typename gil::image + result_img(src_view.width()+(2 * extend_count), src_view.height()+(2 * extend_count)); + typename gil::image::view_t result_view = view(result_img); + + auto original_view = subimage_view( + src_view, + -extend_count, + -extend_count, + src_view.width() + (2 * extend_count), + src_view.height() + (2 * extend_count) + ); + + for (std::ptrdiff_t i = 0; i < result_view.height(); i++) + { + assign_pixels( + original_view.row_begin(i), + original_view.row_end(i), + result_view.row_begin(i) + ); + } + + return result_img; + } + + auto auxilary_img = extend_col(src_view, extend_count, option); + return extend_row(view(auxilary_img), extend_count, option); +} + } } // namespace boost::gil #endif diff --git a/include/boost/gil/extension/numeric/algorithm.hpp b/include/boost/gil/extension/numeric/algorithm.hpp index 2fbd4b0245..821263a80f 100644 --- a/include/boost/gil/extension/numeric/algorithm.hpp +++ b/include/boost/gil/extension/numeric/algorithm.hpp @@ -1,6 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated -// Copyright 2019 Pranam Lashkari +// Copyright 2019-2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -9,434 +9,8 @@ #ifndef BOOST_GIL_EXTENSION_NUMERIC_ALGORITHM_HPP #define BOOST_GIL_EXTENSION_NUMERIC_ALGORITHM_HPP -#include +#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -namespace boost { namespace gil { - -/// \brief Reference proxy associated with a type that has a \p "reference" member type alias. -/// -/// The reference proxy is the reference type, but with stripped-out C++ reference. -/// Models PixelConcept. -template -struct pixel_proxy : std::remove_reference {}; - -/// \brief std::for_each for a pair of iterators -template -BinaryFunction for_each(Iterator1 first1, Iterator1 last1, Iterator2 first2, BinaryFunction f) -{ - while (first1 != last1) - f(*first1++, *first2++); - return f; -} - -template -inline -auto assign_pixels(SrcIterator src, SrcIterator src_end, DstIterator dst) -> DstIterator -{ - for_each(src, src_end, dst, - pixel_assigns_t - < - typename pixel_proxy::value_type>::type, - typename pixel_proxy::value_type>::type - >()); - return dst + (src_end - src); -} - -namespace detail { - -template -struct inner_product_k_t -{ - template - < - class InputIterator1, - class InputIterator2, - class T, - class BinaryOperation1, - class BinaryOperation2 - > - static T apply( - InputIterator1 first1, - InputIterator2 first2, T init, - BinaryOperation1 binary_op1, - BinaryOperation2 binary_op2) - { - init = binary_op1(init, binary_op2(*first1, *first2)); - return inner_product_k_t::template apply( - first1 + 1, first2 + 1, init, binary_op1, binary_op2); - } -}; - -template <> -struct inner_product_k_t<0> -{ - template - < - class InputIterator1, - class InputIterator2, - class T, - class BinaryOperation1, - class BinaryOperation2 - > - static T apply( - InputIterator1 first1, - InputIterator2 first2, - T init, - BinaryOperation1 binary_op1, - BinaryOperation2 binary_op2) - { - return init; - } -}; - -} // namespace detail - -/// static version of std::inner_product -template -< - std::size_t Size, - class InputIterator1, - class InputIterator2, - class T, - class BinaryOperation1, - class BinaryOperation2 -> -BOOST_FORCEINLINE -T inner_product_k( - InputIterator1 first1, - InputIterator2 first2, - T init, - BinaryOperation1 binary_op1, - BinaryOperation2 binary_op2) -{ - return detail::inner_product_k_t::template apply( - first1, first2, init, binary_op1, binary_op2); -} - -/// \brief 1D un-guarded cross-correlation with a variable-size kernel -template -< - typename PixelAccum, - typename SrcIterator, - typename KernelIterator, - typename Size, - typename DstIterator -> -inline -auto correlate_pixels_n( - SrcIterator src_begin, - SrcIterator src_end, - KernelIterator kernel_begin, - Size kernel_size, - DstIterator dst_begin) - -> DstIterator -{ - using src_pixel_ref_t = typename pixel_proxy - < - typename std::iterator_traits::value_type - >::type; - using dst_pixel_ref_t = typename pixel_proxy - < - typename std::iterator_traits::value_type - >::type; - using kernel_value_t = typename std::iterator_traits::value_type; - - PixelAccum accum_zero; - pixel_zeros_t()(accum_zero); - while (src_begin != src_end) - { - pixel_assigns_t()( - std::inner_product( - src_begin, - src_begin + kernel_size, - kernel_begin, - accum_zero, - pixel_plus_t(), - pixel_multiplies_scalar_t()), - *dst_begin); - - ++src_begin; - ++dst_begin; - } - return dst_begin; -} - -/// \brief 1D un-guarded cross-correlation with a fixed-size kernel -template -< - std::size_t Size, - typename PixelAccum, - typename SrcIterator, - typename KernelIterator, - typename DstIterator -> -inline -auto correlate_pixels_k( - SrcIterator src_begin, - SrcIterator src_end, - KernelIterator kernel_begin, - DstIterator dst_begin) - -> DstIterator -{ - using src_pixel_ref_t = typename pixel_proxy - < - typename std::iterator_traits::value_type - >::type; - using dst_pixel_ref_t = typename pixel_proxy - < - typename std::iterator_traits::value_type - >::type; - using kernel_type = typename std::iterator_traits::value_type; - - PixelAccum accum_zero; - pixel_zeros_t()(accum_zero); - while (src_begin != src_end) - { - pixel_assigns_t()( - inner_product_k( - src_begin, - kernel_begin, - accum_zero, - pixel_plus_t(), - pixel_multiplies_scalar_t()), - *dst_begin); - - ++src_begin; - ++dst_begin; - } - return dst_begin; -} - -/// \brief destination is set to be product of the source and a scalar -/// \tparam PixelAccum - TODO -/// \tparam SrcView Models ImageViewConcept -/// \tparam DstView Models MutableImageViewConcept -template -inline -void view_multiplies_scalar(SrcView const& src_view, Scalar const& scalar, DstView const& dst_view) -{ - static_assert(std::is_scalar::value, "Scalar is not scalar"); - BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); - using src_pixel_ref_t = typename pixel_proxy::type; - using dst_pixel_ref_t = typename pixel_proxy::type; - using y_coord_t = typename SrcView::y_coord_t; - - y_coord_t const height = src_view.height(); - for (y_coord_t y = 0; y < height; ++y) - { - typename SrcView::x_iterator it_src = src_view.row_begin(y); - typename DstView::x_iterator it_dst = dst_view.row_begin(y); - typename SrcView::x_iterator it_src_end = src_view.row_end(y); - while (it_src != it_src_end) - { - pixel_assigns_t()( - pixel_multiplies_scalar_t()(*it_src, scalar), - *it_dst); - - ++it_src; - ++it_dst; - } - } -} - - -/// \ingroup ImageAlgorithms -/// \brief Boundary options for image boundary extension -enum class boundary_option -{ - output_ignore, /// do nothing to the output - output_zero, /// set the output to zero - extend_padded, /// assume the source boundaries to be padded already - extend_zero, /// assume the source boundaries to be zero - extend_constant /// assume the source boundaries to be the boundary value -}; - -namespace detail -{ - -template -void extend_row_impl( - SrcView const& src_view, - RltView result_view, - std::size_t extend_count, - boundary_option option) -{ - std::ptrdiff_t extend_count_ = static_cast(extend_count); - - if (option == boundary_option::extend_constant) - { - for (std::ptrdiff_t i = 0; i < result_view.height(); i++) - { - if(i >= extend_count_ && i < extend_count_ + src_view.height()) - { - assign_pixels( - src_view.row_begin(i - extend_count_), - src_view.row_end(i - extend_count_), - result_view.row_begin(i) - ); - } - else if(i < extend_count_) - { - assign_pixels(src_view.row_begin(0), src_view.row_end(0), result_view.row_begin(i)); - } - else - { - assign_pixels( - src_view.row_begin(src_view.height() - 1), - src_view.row_end(src_view.height() - 1), - result_view.row_begin(i) - ); - } - - } - } - else if (option == boundary_option::extend_zero) - { - typename SrcView::value_type acc_zero; - pixel_zeros_t()(acc_zero); - - for (std::ptrdiff_t i = 0; i < result_view.height(); i++) - { - if (i >= extend_count_ && i < extend_count_ + src_view.height()) - { - assign_pixels( - src_view.row_begin(i - extend_count_), - src_view.row_end(i - extend_count_), - result_view.row_begin(i) - ); - } - else - { - std::fill_n(result_view.row_begin(i), result_view.width(), acc_zero); - } - } - } - else if (option == boundary_option::extend_padded) - { - auto original_view = subimage_view( - src_view, - 0, - -extend_count, - src_view.width(), - src_view.height() + (2 * extend_count) - ); - for (std::ptrdiff_t i = 0; i < result_view.height(); i++) - { - assign_pixels( - original_view.row_begin(i), - original_view.row_end(i), - result_view.row_begin(i) - ); - } - } - else - { - BOOST_ASSERT_MSG(false, "Invalid boundary option"); - } -} - -} //namespace detail - - -/// \brief adds new row at top and bottom. -/// Image padding introduces new pixels around the edges of an image. -/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques. -/// \tparam SrcView Models ImageViewConcept -/// \tparam extend_count number of rows to be added each side -/// \tparam option - TODO -template -auto extend_row( - SrcView const& src_view, - std::size_t extend_count, - boundary_option option -) -> typename gil::image -{ - typename gil::image - result_img(src_view.width(), src_view.height() + (2 * extend_count)); - - auto result_view = view(result_img); - detail::extend_row_impl(src_view, result_view, extend_count, option); - return result_img; -} - - -/// \brief adds new column at left and right. -/// Image padding introduces new pixels around the edges of an image. -/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques. -/// \tparam SrcView Models ImageViewConcept -/// \tparam extend_count number of columns to be added each side -/// \tparam option - TODO -template -auto extend_col( - SrcView const& src_view, - std::size_t extend_count, - boundary_option option -) -> typename gil::image -{ - auto src_view_rotate = rotated90cw_view(src_view); - - typename gil::image - result_img(src_view.width() + (2 * extend_count), src_view.height()); - - auto result_view = rotated90cw_view(view(result_img)); - detail::extend_row_impl(src_view_rotate, result_view, extend_count, option); - return result_img; -} - -/// \brief adds new row and column at all sides. -/// Image padding introduces new pixels around the edges of an image. -/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques. -/// \tparam SrcView Models ImageViewConcept -/// \tparam extend_count number of rows/column to be added each side -/// \tparam option - TODO -template -auto extend_boundary( - SrcView const& src_view, - std::size_t extend_count, - boundary_option option -) -> typename gil::image -{ - if (option == boundary_option::extend_padded) - { - typename gil::image - result_img(src_view.width()+(2 * extend_count), src_view.height()+(2 * extend_count)); - typename gil::image::view_t result_view = view(result_img); - - auto original_view = subimage_view( - src_view, - -extend_count, - -extend_count, - src_view.width() + (2 * extend_count), - src_view.height() + (2 * extend_count) - ); - - for (std::ptrdiff_t i = 0; i < result_view.height(); i++) - { - assign_pixels( - original_view.row_begin(i), - original_view.row_end(i), - result_view.row_begin(i) - ); - } - - return result_img; - } - - auto auxilary_img = extend_col(src_view, extend_count, option); - return extend_row(view(auxilary_img), extend_count, option); -} - -}} // namespace boost::gil +BOOST_HEADER_DEPRECATED("") #endif diff --git a/include/boost/gil/extension/numeric/convolve.hpp b/include/boost/gil/extension/numeric/convolve.hpp index 29e7ad1132..86de17a835 100644 --- a/include/boost/gil/extension/numeric/convolve.hpp +++ b/include/boost/gil/extension/numeric/convolve.hpp @@ -1,6 +1,7 @@ // // Copyright 2005-2007 Adobe Systems Incorporated // Copyright 2019 Miral Shah +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -9,7 +10,6 @@ #ifndef BOOST_GIL_EXTENSION_NUMERIC_CONVOLVE_HPP #define BOOST_GIL_EXTENSION_NUMERIC_CONVOLVE_HPP -#include #include #include diff --git a/include/boost/gil/image.hpp b/include/boost/gil/image.hpp index c3cc6818b7..23763d412a 100644 --- a/include/boost/gil/image.hpp +++ b/include/boost/gil/image.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -37,7 +38,7 @@ namespace boost { namespace gil { /// //////////////////////////////////////////////////////////////////////////////////////// -template< typename Pixel, bool IsPlanar = false, typename Alloc=std::allocator > +template< typename Pixel, bool IsPlanar, typename Alloc> class image { public: @@ -348,7 +349,7 @@ class image } view_t _view; // contains pointer to the pixels, the image size and ways to navigate pixels - + // for construction from other type template friend class image; private: diff --git a/include/boost/gil/image_processing/filter.hpp b/include/boost/gil/image_processing/filter.hpp index 7b960731a9..ba9f35e0b5 100644 --- a/include/boost/gil/image_processing/filter.hpp +++ b/include/boost/gil/image_processing/filter.hpp @@ -1,5 +1,6 @@ // // Copyright 2019 Miral Shah +// Copyright 2021 Pranam Lashkari // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,12 +10,12 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_FILTER_HPP #define BOOST_GIL_IMAGE_PROCESSING_FILTER_HPP -#include #include #include #include #include +#include #include #include diff --git a/include/boost/gil/metafunctions.hpp b/include/boost/gil/metafunctions.hpp index d2fe04dc49..9f9a4b9678 100644 --- a/include/boost/gil/metafunctions.hpp +++ b/include/boost/gil/metafunctions.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -27,7 +28,7 @@ template struct planar_pixel_iterator; template class memory_based_step_iterator; template class memory_based_2d_locator; template class image_view; -template class image; +template > class image; template struct channel_type; template struct color_space_type; template struct channel_mapping_type; diff --git a/test/core/algorithm/CMakeLists.txt b/test/core/algorithm/CMakeLists.txt index 688bd93e1c..13e113fc64 100644 --- a/test/core/algorithm/CMakeLists.txt +++ b/test/core/algorithm/CMakeLists.txt @@ -1,5 +1,6 @@ # # Copyright (c) 2018 Mateusz Loskot +# Copyright (c) 2021 Pranam Lashkari # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at @@ -8,7 +9,8 @@ foreach(_name for_each_pixel std_fill - std_uninitialized_fill) + std_uninitialized_fill + extend_boundary) set(_test t_core_algorithm_${_name}) set(_target test_core_algorithm_${_name}) diff --git a/test/core/algorithm/Jamfile b/test/core/algorithm/Jamfile index 8510c80094..99436eca72 100644 --- a/test/core/algorithm/Jamfile +++ b/test/core/algorithm/Jamfile @@ -1,6 +1,7 @@ # Boost.GIL (Generic Image Library) - tests # # Copyright (c) 2018-2019 Mateusz Loskot +# Copyright (c) 2021 Pranam Lashkari # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or @@ -11,3 +12,4 @@ import testing ; run for_each_pixel.cpp ; run std_fill.cpp ; run std_uninitialized_fill.cpp ; +run extend_boundary.cpp ; diff --git a/test/extension/numeric/extend_boundary.cpp b/test/core/algorithm/extend_boundary.cpp similarity index 98% rename from test/extension/numeric/extend_boundary.cpp rename to test/core/algorithm/extend_boundary.cpp index f432ad69ba..7dbe3a082a 100644 --- a/test/extension/numeric/extend_boundary.cpp +++ b/test/core/algorithm/extend_boundary.cpp @@ -1,14 +1,15 @@ // -// Copyright 2019 Pranam Lashkari +// Copyright 2019-2021 Pranam Lashkari // // Distributed under 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; diff --git a/test/extension/numeric/CMakeLists.txt b/test/extension/numeric/CMakeLists.txt index 4702c52c87..03a03d803b 100644 --- a/test/extension/numeric/CMakeLists.txt +++ b/test/extension/numeric/CMakeLists.txt @@ -1,5 +1,6 @@ # # Copyright (c) 2017 Mateusz Loskot +# Copyright (c) 2021 Pranam Lashkari # All rights reserved. # # Distributed under the Boost Software License, Version 1.0. @@ -14,7 +15,6 @@ foreach(_name convolve_2d convolve_cols convolve_rows - extend_boundary kernel kernel_fixed matrix3x2 diff --git a/test/extension/numeric/Jamfile b/test/extension/numeric/Jamfile index 9662357c45..e3557c4fee 100644 --- a/test/extension/numeric/Jamfile +++ b/test/extension/numeric/Jamfile @@ -2,6 +2,7 @@ # # Copyright (c) 2013 Christian Henning # Copyright (c) 2019 Mateusz Loskot +# Copyright (c) 2021 Pranam Lashkari # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or @@ -18,7 +19,6 @@ run convolve.cpp ; run convolve_2d.cpp ; run convolve_cols.cpp ; run convolve_rows.cpp ; -run extend_boundary.cpp ; run kernel.cpp ; run kernel_fixed.cpp ; run matrix3x2.cpp ; From baf246a72681a363fd423cdcccbd97cfc45f07b5 Mon Sep 17 00:00:00 2001 From: Pranam Lashkari Date: Mon, 1 Mar 2021 06:08:21 +0530 Subject: [PATCH 076/193] numeric extension move into core part 2 moved numeric/channel_numeric_operations.hpp into core --- .../boost/gil/channel_numeric_operations.hpp | 253 ++++++++++++++++++ .../numeric/channel_numeric_operations.hpp | 242 +---------------- .../numeric/pixel_numeric_operations.hpp | 3 +- test/core/channel/CMakeLists.txt | 4 +- test/core/channel/Jamfile | 2 + .../channel}/channel_numeric_operations.cpp | 1 + test/extension/numeric/CMakeLists.txt | 1 - test/extension/numeric/Jamfile | 1 - 8 files changed, 264 insertions(+), 243 deletions(-) create mode 100644 include/boost/gil/channel_numeric_operations.hpp rename test/{extension/numeric => core/channel}/channel_numeric_operations.cpp (99%) diff --git a/include/boost/gil/channel_numeric_operations.hpp b/include/boost/gil/channel_numeric_operations.hpp new file mode 100644 index 0000000000..703854710f --- /dev/null +++ b/include/boost/gil/channel_numeric_operations.hpp @@ -0,0 +1,253 @@ +// +// Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari +// +// Distributed under 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_CHANNEL_NUMERIC_OPERATIONS_HPP +#define BOOST_GIL_CHANNEL_NUMERIC_OPERATIONS_HPP + +#include + +namespace boost { namespace gil { + +// Function objects and utilities for channel-wise numeric operations. +// +// List of currently defined functors: +// channel_plus_t (+) +// channel_minus_t (-) +// channel_multiplies_t (*) +// channel_divides_t (/), +// channel_plus_scalar_t (+s) +// channel_minus_scalar_t (-s), +// channel_multiplies_scalar_t (*s) +// channel_divides_scalar_t (/s), +// channel_halves_t (/=2) +// channel_zeros_t (=0) +// channel_assigns_t (=) + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of addition of two channel values. +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_plus_t +{ + using ChannelRef1 = typename channel_traits::const_reference; + using ChannelRef2 = typename channel_traits::const_reference; + static_assert(std::is_convertible::value, + "ChannelRef1 not convertible to ChannelResult"); + static_assert(std::is_convertible::value, + "ChannelRef2 not convertible to ChannelResult"); + + /// \param ch1 - first of the two addends (augend). + /// \param ch2 - second of the two addends. + auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelResult + { + return ChannelResult(ch1) + ChannelResult(ch2); + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of subtraction of two channel values. +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_minus_t +{ + using ChannelRef1 = typename channel_traits::const_reference; + using ChannelRef2 = typename channel_traits::const_reference; + static_assert(std::is_convertible::value, + "ChannelRef1 not convertible to ChannelResult"); + static_assert(std::is_convertible::value, + "ChannelRef2 not convertible to ChannelResult"); + + /// \param ch1 - minuend operand of the subtraction. + /// \param ch2 - subtrahend operand of the subtraction. + auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelResult + { + return ChannelResult(ch1) - ChannelResult(ch2); + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of multiplication of two channel values. +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_multiplies_t +{ + using ChannelRef1 = typename channel_traits::const_reference; + using ChannelRef2 = typename channel_traits::const_reference; + static_assert(std::is_convertible::value, + "ChannelRef1 not convertible to ChannelResult"); + static_assert(std::is_convertible::value, + "ChannelRef2 not convertible to ChannelResult"); + + /// \param ch1 - first of the two factors (multiplicand). + /// \param ch2 - second of the two factors (multiplier). + auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelResult + { + return ChannelResult(ch1) * ChannelResult(ch2); + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of division of two channel values. +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_divides_t +{ + using ChannelRef1 = typename channel_traits::const_reference; + using ChannelRef2 = typename channel_traits::const_reference; + static_assert(std::is_convertible::value, + "ChannelRef1 not convertible to ChannelResult"); + static_assert(std::is_convertible::value, + "ChannelRef2 not convertible to ChannelResult"); + + /// \param ch1 - dividend operand of the two division operation. + /// \param ch2 - divisor operand of the two division operation. + auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelResult + { + return ChannelResult(ch1) / ChannelResult(ch2); + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of adding scalar to channel value. +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_plus_scalar_t +{ + using ChannelRef = typename channel_traits::const_reference; + static_assert(std::is_convertible::value, + "ChannelRef not convertible to ChannelResult"); + static_assert(std::is_scalar::value, "Scalar not a scalar"); + static_assert(std::is_convertible::value, + "Scalar not convertible to ChannelResult"); + + auto operator()(ChannelRef channel, Scalar const& scalar) const -> ChannelResult + { + return ChannelResult(channel) + ChannelResult(scalar); + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of subtracting scalar from channel value. +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_minus_scalar_t +{ + using ChannelRef = typename channel_traits::const_reference; + static_assert(std::is_convertible::value, + "ChannelRef not convertible to ChannelResult"); + static_assert(std::is_scalar::value, "Scalar not a scalar"); + static_assert(std::is_convertible::value, + "Scalar not convertible to ChannelResult"); + + /// \param channel - minuend operand of the subtraction. + /// \param scalar - subtrahend operand of the subtraction. + auto operator()(ChannelRef channel, Scalar const& scalar) const -> ChannelResult + { + // TODO: Convertion after subtraction vs conversion of operands in channel_minus_t? + return ChannelResult(channel - scalar); + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of channel value by a scalar. +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_multiplies_scalar_t +{ + using ChannelRef = typename channel_traits::const_reference; + static_assert(std::is_convertible::value, + "ChannelRef not convertible to ChannelResult"); + static_assert(std::is_scalar::value, "Scalar not a scalar"); + static_assert(std::is_convertible::value, + "Scalar not convertible to ChannelResult"); + + /// \param channel - first of the two factors (multiplicand). + /// \param scalar - second of the two factors (multiplier). + auto operator()(ChannelRef channel, Scalar const& scalar) const -> ChannelResult + { + return ChannelResult(channel) * ChannelResult(scalar); + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of dividing channel value by scalar. +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_divides_scalar_t +{ + using ChannelRef = typename channel_traits::const_reference; + static_assert(std::is_convertible::value, + "ChannelRef not convertible to ChannelResult"); + static_assert(std::is_scalar::value, "Scalar not a scalar"); + static_assert(std::is_convertible::value, + "Scalar not convertible to ChannelResult"); + + /// \param channel - dividend operand of the two division operation. + /// \param scalar - divisor operand of the two division operation. + auto operator()(ChannelRef channel, Scalar const& scalar) const -> ChannelResult + { + return ChannelResult(channel) / ChannelResult(scalar); + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Arithmetic operation of dividing channel value by 2 +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_halves_t +{ + using ChannelRef = typename channel_traits::reference; + + auto operator()(ChannelRef channel) const -> ChannelRef + { + // TODO: Split into steps: extract with explicit conversion to double, divide and assign? + //double const v = ch; + //ch = static_cast(v / 2.0); + channel /= 2.0; + return channel; + } +}; + +/// \ingroup ChannelNumericOperations +/// \brief Operation of setting channel value to zero +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_zeros_t +{ + using ChannelRef = typename channel_traits::reference; + + auto operator()(ChannelRef channel) const -> ChannelRef + { + channel = Channel(0); + return channel; + } +}; + +/// \ingroup ChannelNumericOperations +/// structure for assigning one channel to another +/// \note This is a generic implementation; user should specialize it for better performance. +template +struct channel_assigns_t +{ + using ChannelRef1 = typename channel_traits::const_reference; + using ChannelRef2 = typename channel_traits::reference; + static_assert(std::is_convertible::value, + "ChannelRef1 not convertible to Channel2"); + + /// \param ch1 - assignor side (input) of the assignment operation + /// \param ch2 - assignee side (output) of the assignment operation + auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelRef2 + { + ch2 = Channel2(ch1); + return ch2; + } +}; + +}} // namespace boost::gil + +#endif diff --git a/include/boost/gil/extension/numeric/channel_numeric_operations.hpp b/include/boost/gil/extension/numeric/channel_numeric_operations.hpp index 264ffdd782..6cea3adfeb 100644 --- a/include/boost/gil/extension/numeric/channel_numeric_operations.hpp +++ b/include/boost/gil/extension/numeric/channel_numeric_operations.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -8,245 +9,8 @@ #ifndef BOOST_GIL_EXTENSION_NUMERIC_CHANNEL_NUMERIC_OPERATIONS_HPP #define BOOST_GIL_EXTENSION_NUMERIC_CHANNEL_NUMERIC_OPERATIONS_HPP -#include +#include -namespace boost { namespace gil { - -// Function objects and utilities for channel-wise numeric operations. -// -// List of currently defined functors: -// channel_plus_t (+) -// channel_minus_t (-) -// channel_multiplies_t (*) -// channel_divides_t (/), -// channel_plus_scalar_t (+s) -// channel_minus_scalar_t (-s), -// channel_multiplies_scalar_t (*s) -// channel_divides_scalar_t (/s), -// channel_halves_t (/=2) -// channel_zeros_t (=0) -// channel_assigns_t (=) - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of addition of two channel values. -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_plus_t -{ - using ChannelRef1 = typename channel_traits::const_reference; - using ChannelRef2 = typename channel_traits::const_reference; - static_assert(std::is_convertible::value, - "ChannelRef1 not convertible to ChannelResult"); - static_assert(std::is_convertible::value, - "ChannelRef2 not convertible to ChannelResult"); - - /// \param ch1 - first of the two addends (augend). - /// \param ch2 - second of the two addends. - auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelResult - { - return ChannelResult(ch1) + ChannelResult(ch2); - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of subtraction of two channel values. -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_minus_t -{ - using ChannelRef1 = typename channel_traits::const_reference; - using ChannelRef2 = typename channel_traits::const_reference; - static_assert(std::is_convertible::value, - "ChannelRef1 not convertible to ChannelResult"); - static_assert(std::is_convertible::value, - "ChannelRef2 not convertible to ChannelResult"); - - /// \param ch1 - minuend operand of the subtraction. - /// \param ch2 - subtrahend operand of the subtraction. - auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelResult - { - return ChannelResult(ch1) - ChannelResult(ch2); - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of multiplication of two channel values. -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_multiplies_t -{ - using ChannelRef1 = typename channel_traits::const_reference; - using ChannelRef2 = typename channel_traits::const_reference; - static_assert(std::is_convertible::value, - "ChannelRef1 not convertible to ChannelResult"); - static_assert(std::is_convertible::value, - "ChannelRef2 not convertible to ChannelResult"); - - /// \param ch1 - first of the two factors (multiplicand). - /// \param ch2 - second of the two factors (multiplier). - auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelResult - { - return ChannelResult(ch1) * ChannelResult(ch2); - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of division of two channel values. -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_divides_t -{ - using ChannelRef1 = typename channel_traits::const_reference; - using ChannelRef2 = typename channel_traits::const_reference; - static_assert(std::is_convertible::value, - "ChannelRef1 not convertible to ChannelResult"); - static_assert(std::is_convertible::value, - "ChannelRef2 not convertible to ChannelResult"); - - /// \param ch1 - dividend operand of the two division operation. - /// \param ch2 - divisor operand of the two division operation. - auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelResult - { - return ChannelResult(ch1) / ChannelResult(ch2); - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of adding scalar to channel value. -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_plus_scalar_t -{ - using ChannelRef = typename channel_traits::const_reference; - static_assert(std::is_convertible::value, - "ChannelRef not convertible to ChannelResult"); - static_assert(std::is_scalar::value, "Scalar not a scalar"); - static_assert(std::is_convertible::value, - "Scalar not convertible to ChannelResult"); - - auto operator()(ChannelRef channel, Scalar const& scalar) const -> ChannelResult - { - return ChannelResult(channel) + ChannelResult(scalar); - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of subtracting scalar from channel value. -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_minus_scalar_t -{ - using ChannelRef = typename channel_traits::const_reference; - static_assert(std::is_convertible::value, - "ChannelRef not convertible to ChannelResult"); - static_assert(std::is_scalar::value, "Scalar not a scalar"); - static_assert(std::is_convertible::value, - "Scalar not convertible to ChannelResult"); - - /// \param channel - minuend operand of the subtraction. - /// \param scalar - subtrahend operand of the subtraction. - auto operator()(ChannelRef channel, Scalar const& scalar) const -> ChannelResult - { - // TODO: Convertion after subtraction vs conversion of operands in channel_minus_t? - return ChannelResult(channel - scalar); - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of channel value by a scalar. -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_multiplies_scalar_t -{ - using ChannelRef = typename channel_traits::const_reference; - static_assert(std::is_convertible::value, - "ChannelRef not convertible to ChannelResult"); - static_assert(std::is_scalar::value, "Scalar not a scalar"); - static_assert(std::is_convertible::value, - "Scalar not convertible to ChannelResult"); - - /// \param channel - first of the two factors (multiplicand). - /// \param scalar - second of the two factors (multiplier). - auto operator()(ChannelRef channel, Scalar const& scalar) const -> ChannelResult - { - return ChannelResult(channel) * ChannelResult(scalar); - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of dividing channel value by scalar. -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_divides_scalar_t -{ - using ChannelRef = typename channel_traits::const_reference; - static_assert(std::is_convertible::value, - "ChannelRef not convertible to ChannelResult"); - static_assert(std::is_scalar::value, "Scalar not a scalar"); - static_assert(std::is_convertible::value, - "Scalar not convertible to ChannelResult"); - - /// \param channel - dividend operand of the two division operation. - /// \param scalar - divisor operand of the two division operation. - auto operator()(ChannelRef channel, Scalar const& scalar) const -> ChannelResult - { - return ChannelResult(channel) / ChannelResult(scalar); - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Arithmetic operation of dividing channel value by 2 -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_halves_t -{ - using ChannelRef = typename channel_traits::reference; - - auto operator()(ChannelRef channel) const -> ChannelRef - { - // TODO: Split into steps: extract with explicit conversion to double, divide and assign? - //double const v = ch; - //ch = static_cast(v / 2.0); - channel /= 2.0; - return channel; - } -}; - -/// \ingroup ChannelNumericOperations -/// \brief Operation of setting channel value to zero -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_zeros_t -{ - using ChannelRef = typename channel_traits::reference; - - auto operator()(ChannelRef channel) const -> ChannelRef - { - channel = Channel(0); - return channel; - } -}; - -/// \ingroup ChannelNumericOperations -/// structure for assigning one channel to another -/// \note This is a generic implementation; user should specialize it for better performance. -template -struct channel_assigns_t -{ - using ChannelRef1 = typename channel_traits::const_reference; - using ChannelRef2 = typename channel_traits::reference; - static_assert(std::is_convertible::value, - "ChannelRef1 not convertible to Channel2"); - - /// \param ch1 - assignor side (input) of the assignment operation - /// \param ch2 - assignee side (output) of the assignment operation - auto operator()(ChannelRef1 ch1, ChannelRef2 ch2) const -> ChannelRef2 - { - ch2 = Channel2(ch1); - return ch2; - } -}; - -}} // namespace boost::gil +BOOST_HEADER_DEPRECATED("") #endif diff --git a/include/boost/gil/extension/numeric/pixel_numeric_operations.hpp b/include/boost/gil/extension/numeric/pixel_numeric_operations.hpp index 24c983411b..c3f555d004 100644 --- a/include/boost/gil/extension/numeric/pixel_numeric_operations.hpp +++ b/include/boost/gil/extension/numeric/pixel_numeric_operations.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -8,7 +9,7 @@ #ifndef BOOST_GIL_EXTENSION_NUMERIC_PIXEL_NUMERIC_OPERATIONS_HPP #define BOOST_GIL_EXTENSION_NUMERIC_PIXEL_NUMERIC_OPERATIONS_HPP -#include +#include #include #include diff --git a/test/core/channel/CMakeLists.txt b/test/core/channel/CMakeLists.txt index 365a8274f6..d732e8e8ff 100644 --- a/test/core/channel/CMakeLists.txt +++ b/test/core/channel/CMakeLists.txt @@ -1,5 +1,6 @@ # # Copyright (c) 2018 Mateusz Loskot +# Copyright (c) 2021 Pranam Lashkari # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at @@ -16,7 +17,8 @@ foreach(_name is_channel_integral packed_channel_value scoped_channel_value - test_fixture) + test_fixture + channel_numeric_operations) set(_test t_core_channel_${_name}) set(_target test_core_channel_${_name}) diff --git a/test/core/channel/Jamfile b/test/core/channel/Jamfile index 9e6b65027d..428b9c29f8 100644 --- a/test/core/channel/Jamfile +++ b/test/core/channel/Jamfile @@ -1,6 +1,7 @@ # Boost.GIL (Generic Image Library) - tests # # Copyright (c) 2018-2020 Mateusz Loskot +# Copyright (c) 2021 Pranam Lashkari # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or @@ -15,6 +16,7 @@ run channel_traits.cpp ; run test_fixture.cpp ; run packed_channel_value.cpp ; run scoped_channel_value.cpp ; +run channel_numeric_operations.cpp ; run algorithm_channel_arithmetic.cpp ; run algorithm_channel_convert.cpp ; diff --git a/test/extension/numeric/channel_numeric_operations.cpp b/test/core/channel/channel_numeric_operations.cpp similarity index 99% rename from test/extension/numeric/channel_numeric_operations.cpp rename to test/core/channel/channel_numeric_operations.cpp index 4f75cbbc66..9b4d4fccdc 100644 --- a/test/extension/numeric/channel_numeric_operations.cpp +++ b/test/core/channel/channel_numeric_operations.cpp @@ -1,5 +1,6 @@ // // Copyright 2019-2020 Mateusz Loskot +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at diff --git a/test/extension/numeric/CMakeLists.txt b/test/extension/numeric/CMakeLists.txt index 03a03d803b..17dc7bd2c7 100644 --- a/test/extension/numeric/CMakeLists.txt +++ b/test/extension/numeric/CMakeLists.txt @@ -10,7 +10,6 @@ message(STATUS "Boost.GIL: Configuring tests in test/extension/numeric") foreach(_name - channel_numeric_operations convolve convolve_2d convolve_cols diff --git a/test/extension/numeric/Jamfile b/test/extension/numeric/Jamfile index e3557c4fee..175156e002 100644 --- a/test/extension/numeric/Jamfile +++ b/test/extension/numeric/Jamfile @@ -14,7 +14,6 @@ alias headers : [ generate_self_contained_headers extension/numeric ] ; compile-fail kernel_1d_fixed_even_size_fail.cpp ; -run channel_numeric_operations.cpp ; run convolve.cpp ; run convolve_2d.cpp ; run convolve_cols.cpp ; From 483915cb20174b87109f1852c6072ea1f5ab4209 Mon Sep 17 00:00:00 2001 From: Pranam Lashkari Date: Mon, 1 Mar 2021 16:53:21 +0530 Subject: [PATCH 077/193] numeric extension move into core part 3 moved numeric/convolve.hpp into core --- example/convolution.cpp | 3 +- example/convolve2d.cpp | 15 +- example/harris.cpp | 3 +- example/sobel_scharr.cpp | 14 +- .../boost/gil/extension/numeric/convolve.hpp | 434 +---------------- .../boost/gil/image_processing/convolve.hpp | 445 ++++++++++++++++++ include/boost/gil/image_processing/filter.hpp | 3 +- .../boost/gil/image_processing/numeric.hpp | 3 +- .../boost/gil/image_processing/threshold.hpp | 3 +- test/core/algorithm/extend_boundary.cpp | 2 +- test/core/image_processing/CMakeLists.txt | 7 +- test/core/image_processing/Jamfile | 5 + .../image_processing}/convolve.cpp | 3 +- .../image_processing}/convolve_2d.cpp | 3 +- .../image_processing}/convolve_cols.cpp | 3 +- .../image_processing}/convolve_rows.cpp | 3 +- .../image_processing}/test_fixture.hpp | 1 + test/extension/numeric/CMakeLists.txt | 6 +- test/extension/numeric/Jamfile | 4 - 19 files changed, 495 insertions(+), 465 deletions(-) create mode 100644 include/boost/gil/image_processing/convolve.hpp rename test/{extension/numeric => core/image_processing}/convolve.cpp (97%) rename test/{extension/numeric => core/image_processing}/convolve_2d.cpp (96%) rename test/{extension/numeric => core/image_processing}/convolve_cols.cpp (95%) rename test/{extension/numeric => core/image_processing}/convolve_rows.cpp (95%) rename test/{extension/numeric => core/image_processing}/test_fixture.hpp (93%) diff --git a/example/convolution.cpp b/example/convolution.cpp index b5e911da97..5093ccfcde 100644 --- a/example/convolution.cpp +++ b/example/convolution.cpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -8,7 +9,7 @@ #include #include #include -#include +#include // Convolves the image with a Gaussian kernel. diff --git a/example/convolve2d.cpp b/example/convolve2d.cpp index 9d729b7366..e9e4422008 100644 --- a/example/convolve2d.cpp +++ b/example/convolve2d.cpp @@ -1,15 +1,16 @@ -// +// // Copyright 2019 Miral Shah -// -// Distributed under 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 -// +// Copyright 2019 Mateusz Loskot +// Copyright 2021 Pranam Lashkari +// +// 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 #include #include diff --git a/example/harris.cpp b/example/harris.cpp index 532c6e8d25..0b9ebe6828 100644 --- a/example/harris.cpp +++ b/example/harris.cpp @@ -1,5 +1,6 @@ // // Copyright 2019 Olzhas Zhumabek +// Copyright 2021 Pranam Lashkari // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -7,8 +8,8 @@ // #include -#include #include +#include #include #include #include diff --git a/example/sobel_scharr.cpp b/example/sobel_scharr.cpp index b37694402f..c22480d799 100644 --- a/example/sobel_scharr.cpp +++ b/example/sobel_scharr.cpp @@ -1,15 +1,15 @@ -// -// // Copyright 2019 Olzhas Zhumabek -// -// Distributed under 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 // +// Copyright 2019 Olzhas Zhumabek +// Copyright 2021 Pranam Lashkari +// +// Distributed under 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 #include #include diff --git a/include/boost/gil/extension/numeric/convolve.hpp b/include/boost/gil/extension/numeric/convolve.hpp index 86de17a835..ba8a6f08fd 100644 --- a/include/boost/gil/extension/numeric/convolve.hpp +++ b/include/boost/gil/extension/numeric/convolve.hpp @@ -1,7 +1,7 @@ // // Copyright 2005-2007 Adobe Systems Incorporated // Copyright 2019 Miral Shah -// Copyright 2021 Pranam Lashkari +// Copyright 2019-2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -10,436 +10,8 @@ #ifndef BOOST_GIL_EXTENSION_NUMERIC_CONVOLVE_HPP #define BOOST_GIL_EXTENSION_NUMERIC_CONVOLVE_HPP -#include -#include +#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace boost { namespace gil { - -// 2D spatial seperable convolutions and cross-correlations - -namespace detail { - -/// \brief Computes the cross-correlation of 1D kernel with rows of an image. -/// \tparam PixelAccum - Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView - Specifies the type of gil view of source image which is to be row correlated -/// with the kernel. -/// \tparam Kernel - Specifies the type of 1D kernel which will be row correlated with source image. -/// \tparam DstView - Specifies the type of gil view which will store the result of row -/// correlation between source image and kernel. -/// \tparam Correlator - Specifies the type of correlator which should be used for performing -/// correlation. -/// \param src_view - Gil view of source image used in correlation. -/// \param kernel - 1D kernel which will be correlated with source image. -/// \param dst_view - Gil view which will store the result of row correlation between "src_view" -/// and "kernel". -/// \param option - Specifies the manner in which boundary pixels of "dst_view" should be computed. -/// \param correlator - Correlator which will be used for performing correlation. -template -< - typename PixelAccum, - typename SrcView, - typename Kernel, - typename DstView, - typename Correlator -> -void correlate_rows_impl( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option, - Correlator correlator) -{ - BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); - BOOST_ASSERT(kernel.size() != 0); - - if(kernel.size() == 1) - { - // Reduces to a multiplication - view_multiplies_scalar(src_view, *kernel.begin(), dst_view); - return; - } - - using src_pixel_ref_t = typename pixel_proxy::type; - using dst_pixel_ref_t = typename pixel_proxy::type; - using x_coord_t = typename SrcView::x_coord_t; - using y_coord_t = typename SrcView::y_coord_t; - - x_coord_t const width = src_view.width(); - y_coord_t const height = src_view.height(); - if (width == 0) - return; - - PixelAccum acc_zero; - pixel_zeros_t()(acc_zero); - if (option == boundary_option::output_ignore || option == boundary_option::output_zero) - { - typename DstView::value_type dst_zero; - pixel_assigns_t()(acc_zero, dst_zero); - if (width < static_cast(kernel.size())) - { - if (option == boundary_option::output_zero) - fill_pixels(dst_view, dst_zero); - } - else - { - std::vector buffer(width); - for (y_coord_t y = 0; y < height; ++y) - { - assign_pixels(src_view.row_begin(y), src_view.row_end(y), &buffer.front()); - typename DstView::x_iterator it_dst = dst_view.row_begin(y); - if (option == boundary_option::output_zero) - std::fill_n(it_dst, kernel.left_size(), dst_zero); - it_dst += kernel.left_size(); - correlator(&buffer.front(), &buffer.front() + width + 1 - kernel.size(), - kernel.begin(), it_dst); - it_dst += width + 1 - kernel.size(); - if (option == boundary_option::output_zero) - std::fill_n(it_dst, kernel.right_size(), dst_zero); - } - } - } - else - { - std::vector buffer(width + kernel.size() - 1); - for (y_coord_t y = 0; y < height; ++y) - { - PixelAccum *it_buffer = &buffer.front(); - if (option == boundary_option::extend_padded) - { - assign_pixels( - src_view.row_begin(y) - kernel.left_size(), - src_view.row_end(y) + kernel.right_size(), - it_buffer); - } - else if (option == boundary_option::extend_zero) - { - std::fill_n(it_buffer, kernel.left_size(), acc_zero); - it_buffer += kernel.left_size(); - assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer); - it_buffer += width; - std::fill_n(it_buffer, kernel.right_size(), acc_zero); - } - else if (option == boundary_option::extend_constant) - { - PixelAccum filler; - pixel_assigns_t()(*src_view.row_begin(y), filler); - std::fill_n(it_buffer, kernel.left_size(), filler); - it_buffer += kernel.left_size(); - assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer); - it_buffer += width; - pixel_assigns_t()(src_view.row_end(y)[-1], filler); - std::fill_n(it_buffer, kernel.right_size(), filler); - } - - correlator( - &buffer.front(), &buffer.front() + width, - kernel.begin(), - dst_view.row_begin(y)); - } - } -} - -/// \brief Provides functionality for performing 1D correlation between the kernel and a buffer -/// storing row pixels of source image. Kernel size is to be provided through constructor for all -/// instances. -template -class correlator_n -{ -public: - correlator_n(std::size_t size) : size_(size) {} - - template - void operator()( - SrcIterator src_begin, - SrcIterator src_end, - KernelIterator kernel_begin, - DstIterator dst_begin) - { - correlate_pixels_n(src_begin, src_end, kernel_begin, size_, dst_begin); - } - -private: - std::size_t size_{0}; -}; - -/// \brief Provides functionality for performing 1D correlation between the kernel and a buffer -/// storing row pixels of source image. Kernel size is a template parameter and must be -/// compulsorily specified while using. -template -struct correlator_k -{ - template - void operator()( - SrcIterator src_begin, - SrcIterator src_end, - KernelIterator kernel_begin, - DstIterator dst_begin) - { - correlate_pixels_k(src_begin, src_end, kernel_begin, dst_begin); - } -}; - -} // namespace detail - -/// \ingroup ImageAlgorithms -/// \brief Correlate 1D variable-size kernel along the rows of image. -/// \tparam PixelAccum Specifies tha data type which will be used while creating buffer container -/// which is utilized for holding source image pixels after applying appropriate boundary -/// manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be row correlated with source image. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void correlate_rows( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - detail::correlate_rows_impl( - src_view, kernel, dst_view, option, detail::correlator_n(kernel.size())); -} - -/// \ingroup ImageAlgorithms -/// \brief Correlates 1D variable-size kernel along the columns of image. -/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be column correlated with source -/// image. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void correlate_cols( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - correlate_rows( - transposed_view(src_view), kernel, transposed_view(dst_view), option); -} - -/// \ingroup ImageAlgorithms -/// \brief Convolves 1D variable-size kernel along the rows of image. -/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be row convoluted with source image. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void convolve_rows( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - correlate_rows(src_view, reverse_kernel(kernel), dst_view, option); -} - -/// \ingroup ImageAlgorithms -/// \brief Convolves 1D variable-size kernel along the columns of image. -/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be column convoluted with source -/// image. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void convolve_cols( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - convolve_rows( - transposed_view(src_view), kernel, transposed_view(dst_view), option); -} - -/// \ingroup ImageAlgorithms -/// \brief Correlate 1D fixed-size kernel along the rows of image. -/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be row correlated with source image. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void correlate_rows_fixed( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - using correlator = detail::correlator_k; - detail::correlate_rows_impl(src_view, kernel, dst_view, option, correlator{}); -} - -/// \ingroup ImageAlgorithms -/// \brief Correlate 1D fixed-size kernel along the columns of image -/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be column correlated with source -/// image. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void correlate_cols_fixed( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - correlate_rows_fixed( - transposed_view(src_view), kernel, transposed_view(dst_view), option); -} - -/// \ingroup ImageAlgorithms -/// \brief Convolve 1D fixed-size kernel along the rows of image -/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be row convolved with source image. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void convolve_rows_fixed( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - correlate_rows_fixed(src_view, reverse_kernel(kernel), dst_view, option); -} - -/// \ingroup ImageAlgorithms -/// \brief Convolve 1D fixed-size kernel along the columns of image -/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be column convolved with source -/// image. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void convolve_cols_fixed( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - convolve_rows_fixed( - transposed_view(src_view), kernel, transposed_view(dst_view), option); -} - -namespace detail -{ - -/// \ingroup ImageAlgorithms -/// \brief Convolve 1D variable-size kernel along both rows and columns of image -/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container -/// utilized for holding source image pixels after applying appropriate boundary manipulations. -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 1D kernel which will be used for 1D row and column -/// convolution. -/// \tparam DstView Models MutableImageViewConcept -template -BOOST_FORCEINLINE -void convolve_1d( - SrcView const& src_view, - Kernel const& kernel, - DstView const& dst_view, - boundary_option option = boundary_option::extend_zero) -{ - convolve_rows(src_view, kernel, dst_view, option); - convolve_cols(dst_view, kernel, dst_view, option); -} - -template -void convolve_2d_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel) -{ - int flip_ker_row, flip_ker_col, row_boundary, col_boundary; - float aux_total; - for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) - { - for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) - { - aux_total = 0.0f; - for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row) - { - flip_ker_row = kernel.size() - 1 - kernel_row; // row index of flipped kernel - - for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col) - { - flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel - - // index of input signal, used for checking boundary - row_boundary = view_row + (kernel.center_y() - flip_ker_row); - col_boundary = view_col + (kernel.center_x() - flip_ker_col); - - // ignore input samples which are out of bound - if (row_boundary >= 0 && row_boundary < src_view.height() && - col_boundary >= 0 && col_boundary < src_view.width()) - { - aux_total += - src_view(col_boundary, row_boundary)[0] * - kernel.at(flip_ker_row, flip_ker_col); - } - } - } - dst_view(view_col, view_row) = aux_total; - } - } -} - -/// \ingroup ImageAlgorithms -/// \brief convolve_2d can only use convolve_option_extend_zero as convolve_boundary_option -/// this is the default option and cannot be changed for now -/// (In future there are plans to improve the algorithm and allow user to use other options as well) -/// \tparam SrcView Models ImageViewConcept -/// \tparam Kernel Specifies the type of 2D kernel which will be used while convolution. -/// \tparam DstView Models MutableImageViewConcept -template -void convolve_2d(SrcView const& src_view, Kernel const& kernel, DstView const& dst_view) -{ - BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); - BOOST_ASSERT(kernel.size() != 0); - - 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"); - - for (std::size_t i = 0; i < src_view.num_channels(); i++) - { - detail::convolve_2d_impl( - nth_channel_view(src_view, i), - nth_channel_view(dst_view, i), - kernel - ); - } -} - -}}} // namespace boost::gil::detail +BOOST_HEADER_DEPRECATED("") #endif diff --git a/include/boost/gil/image_processing/convolve.hpp b/include/boost/gil/image_processing/convolve.hpp new file mode 100644 index 0000000000..3880fc3bb1 --- /dev/null +++ b/include/boost/gil/image_processing/convolve.hpp @@ -0,0 +1,445 @@ +// +// Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2019 Miral Shah +// Copyright 2019-2021 Pranam Lashkari +// +// Distributed under 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_CONVOLVE_HPP +#define BOOST_GIL_IMAGE_PROCESSING_CONVOLVE_HPP + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace boost { namespace gil { + +// 2D spatial seperable convolutions and cross-correlations + +namespace detail { + +/// \brief Computes the cross-correlation of 1D kernel with rows of an image. +/// \tparam PixelAccum - Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView - Specifies the type of gil view of source image which is to be row correlated +/// with the kernel. +/// \tparam Kernel - Specifies the type of 1D kernel which will be row correlated with source image. +/// \tparam DstView - Specifies the type of gil view which will store the result of row +/// correlation between source image and kernel. +/// \tparam Correlator - Specifies the type of correlator which should be used for performing +/// correlation. +/// \param src_view - Gil view of source image used in correlation. +/// \param kernel - 1D kernel which will be correlated with source image. +/// \param dst_view - Gil view which will store the result of row correlation between "src_view" +/// and "kernel". +/// \param option - Specifies the manner in which boundary pixels of "dst_view" should be computed. +/// \param correlator - Correlator which will be used for performing correlation. +template +< + typename PixelAccum, + typename SrcView, + typename Kernel, + typename DstView, + typename Correlator +> +void correlate_rows_impl( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option, + Correlator correlator) +{ + BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + BOOST_ASSERT(kernel.size() != 0); + + if(kernel.size() == 1) + { + // Reduces to a multiplication + view_multiplies_scalar(src_view, *kernel.begin(), dst_view); + return; + } + + using src_pixel_ref_t = typename pixel_proxy::type; + using dst_pixel_ref_t = typename pixel_proxy::type; + using x_coord_t = typename SrcView::x_coord_t; + using y_coord_t = typename SrcView::y_coord_t; + + x_coord_t const width = src_view.width(); + y_coord_t const height = src_view.height(); + if (width == 0) + return; + + PixelAccum acc_zero; + pixel_zeros_t()(acc_zero); + if (option == boundary_option::output_ignore || option == boundary_option::output_zero) + { + typename DstView::value_type dst_zero; + pixel_assigns_t()(acc_zero, dst_zero); + if (width < static_cast(kernel.size())) + { + if (option == boundary_option::output_zero) + fill_pixels(dst_view, dst_zero); + } + else + { + std::vector buffer(width); + for (y_coord_t y = 0; y < height; ++y) + { + assign_pixels(src_view.row_begin(y), src_view.row_end(y), &buffer.front()); + typename DstView::x_iterator it_dst = dst_view.row_begin(y); + if (option == boundary_option::output_zero) + std::fill_n(it_dst, kernel.left_size(), dst_zero); + it_dst += kernel.left_size(); + correlator(&buffer.front(), &buffer.front() + width + 1 - kernel.size(), + kernel.begin(), it_dst); + it_dst += width + 1 - kernel.size(); + if (option == boundary_option::output_zero) + std::fill_n(it_dst, kernel.right_size(), dst_zero); + } + } + } + else + { + std::vector buffer(width + kernel.size() - 1); + for (y_coord_t y = 0; y < height; ++y) + { + PixelAccum *it_buffer = &buffer.front(); + if (option == boundary_option::extend_padded) + { + assign_pixels( + src_view.row_begin(y) - kernel.left_size(), + src_view.row_end(y) + kernel.right_size(), + it_buffer); + } + else if (option == boundary_option::extend_zero) + { + std::fill_n(it_buffer, kernel.left_size(), acc_zero); + it_buffer += kernel.left_size(); + assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer); + it_buffer += width; + std::fill_n(it_buffer, kernel.right_size(), acc_zero); + } + else if (option == boundary_option::extend_constant) + { + PixelAccum filler; + pixel_assigns_t()(*src_view.row_begin(y), filler); + std::fill_n(it_buffer, kernel.left_size(), filler); + it_buffer += kernel.left_size(); + assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer); + it_buffer += width; + pixel_assigns_t()(src_view.row_end(y)[-1], filler); + std::fill_n(it_buffer, kernel.right_size(), filler); + } + + correlator( + &buffer.front(), &buffer.front() + width, + kernel.begin(), + dst_view.row_begin(y)); + } + } +} + +/// \brief Provides functionality for performing 1D correlation between the kernel and a buffer +/// storing row pixels of source image. Kernel size is to be provided through constructor for all +/// instances. +template +class correlator_n +{ +public: + correlator_n(std::size_t size) : size_(size) {} + + template + void operator()( + SrcIterator src_begin, + SrcIterator src_end, + KernelIterator kernel_begin, + DstIterator dst_begin) + { + correlate_pixels_n(src_begin, src_end, kernel_begin, size_, dst_begin); + } + +private: + std::size_t size_{0}; +}; + +/// \brief Provides functionality for performing 1D correlation between the kernel and a buffer +/// storing row pixels of source image. Kernel size is a template parameter and must be +/// compulsorily specified while using. +template +struct correlator_k +{ + template + void operator()( + SrcIterator src_begin, + SrcIterator src_end, + KernelIterator kernel_begin, + DstIterator dst_begin) + { + correlate_pixels_k(src_begin, src_end, kernel_begin, dst_begin); + } +}; + +} // namespace detail + +/// \ingroup ImageAlgorithms +/// \brief Correlate 1D variable-size kernel along the rows of image. +/// \tparam PixelAccum Specifies tha data type which will be used while creating buffer container +/// which is utilized for holding source image pixels after applying appropriate boundary +/// manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be row correlated with source image. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void correlate_rows( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + detail::correlate_rows_impl( + src_view, kernel, dst_view, option, detail::correlator_n(kernel.size())); +} + +/// \ingroup ImageAlgorithms +/// \brief Correlates 1D variable-size kernel along the columns of image. +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be column correlated with source +/// image. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void correlate_cols( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + correlate_rows( + transposed_view(src_view), kernel, transposed_view(dst_view), option); +} + +/// \ingroup ImageAlgorithms +/// \brief Convolves 1D variable-size kernel along the rows of image. +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be row convoluted with source image. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void convolve_rows( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + correlate_rows(src_view, reverse_kernel(kernel), dst_view, option); +} + +/// \ingroup ImageAlgorithms +/// \brief Convolves 1D variable-size kernel along the columns of image. +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be column convoluted with source +/// image. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void convolve_cols( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + convolve_rows( + transposed_view(src_view), kernel, transposed_view(dst_view), option); +} + +/// \ingroup ImageAlgorithms +/// \brief Correlate 1D fixed-size kernel along the rows of image. +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be row correlated with source image. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void correlate_rows_fixed( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + using correlator = detail::correlator_k; + detail::correlate_rows_impl(src_view, kernel, dst_view, option, correlator{}); +} + +/// \ingroup ImageAlgorithms +/// \brief Correlate 1D fixed-size kernel along the columns of image +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be column correlated with source +/// image. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void correlate_cols_fixed( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + correlate_rows_fixed( + transposed_view(src_view), kernel, transposed_view(dst_view), option); +} + +/// \ingroup ImageAlgorithms +/// \brief Convolve 1D fixed-size kernel along the rows of image +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be row convolved with source image. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void convolve_rows_fixed( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + correlate_rows_fixed(src_view, reverse_kernel(kernel), dst_view, option); +} + +/// \ingroup ImageAlgorithms +/// \brief Convolve 1D fixed-size kernel along the columns of image +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be column convolved with source +/// image. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void convolve_cols_fixed( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + convolve_rows_fixed( + transposed_view(src_view), kernel, transposed_view(dst_view), option); +} + +namespace detail +{ + +/// \ingroup ImageAlgorithms +/// \brief Convolve 1D variable-size kernel along both rows and columns of image +/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container +/// utilized for holding source image pixels after applying appropriate boundary manipulations. +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 1D kernel which will be used for 1D row and column +/// convolution. +/// \tparam DstView Models MutableImageViewConcept +template +BOOST_FORCEINLINE +void convolve_1d( + SrcView const& src_view, + Kernel const& kernel, + DstView const& dst_view, + boundary_option option = boundary_option::extend_zero) +{ + convolve_rows(src_view, kernel, dst_view, option); + convolve_cols(dst_view, kernel, dst_view, option); +} + +template +void convolve_2d_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel) +{ + int flip_ker_row, flip_ker_col, row_boundary, col_boundary; + float aux_total; + for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) + { + for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) + { + aux_total = 0.0f; + for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row) + { + flip_ker_row = kernel.size() - 1 - kernel_row; // row index of flipped kernel + + for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col) + { + flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel + + // index of input signal, used for checking boundary + row_boundary = view_row + (kernel.center_y() - flip_ker_row); + col_boundary = view_col + (kernel.center_x() - flip_ker_col); + + // ignore input samples which are out of bound + if (row_boundary >= 0 && row_boundary < src_view.height() && + col_boundary >= 0 && col_boundary < src_view.width()) + { + aux_total += + src_view(col_boundary, row_boundary)[0] * + kernel.at(flip_ker_row, flip_ker_col); + } + } + } + dst_view(view_col, view_row) = aux_total; + } + } +} + +/// \ingroup ImageAlgorithms +/// \brief convolve_2d can only use convolve_option_extend_zero as convolve_boundary_option +/// this is the default option and cannot be changed for now +/// (In future there are plans to improve the algorithm and allow user to use other options as well) +/// \tparam SrcView Models ImageViewConcept +/// \tparam Kernel Specifies the type of 2D kernel which will be used while convolution. +/// \tparam DstView Models MutableImageViewConcept +template +void convolve_2d(SrcView const& src_view, Kernel const& kernel, DstView const& dst_view) +{ + BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + BOOST_ASSERT(kernel.size() != 0); + + 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"); + + for (std::size_t i = 0; i < src_view.num_channels(); i++) + { + detail::convolve_2d_impl( + nth_channel_view(src_view, i), + nth_channel_view(dst_view, i), + kernel + ); + } +} + +}}} // namespace boost::gil::detail + +#endif diff --git a/include/boost/gil/image_processing/filter.hpp b/include/boost/gil/image_processing/filter.hpp index ba9f35e0b5..097a85f1c7 100644 --- a/include/boost/gil/image_processing/filter.hpp +++ b/include/boost/gil/image_processing/filter.hpp @@ -11,7 +11,8 @@ #define BOOST_GIL_IMAGE_PROCESSING_FILTER_HPP #include -#include + +#include #include #include diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index b601b17dd0..6241c8490b 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -1,5 +1,6 @@ // // Copyright 2019 Olzhas Zhumabek +// Copyright 2021 Pranam Lashkari // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,7 +10,7 @@ #define BOOST_GIL_IMAGE_PROCESSING_NUMERIC_HPP #include -#include +#include #include #include #include diff --git a/include/boost/gil/image_processing/threshold.hpp b/include/boost/gil/image_processing/threshold.hpp index 82ea4d0eca..78809f6d38 100644 --- a/include/boost/gil/image_processing/threshold.hpp +++ b/include/boost/gil/image_processing/threshold.hpp @@ -1,5 +1,6 @@ // // Copyright 2019 Miral Shah +// Copyright 2021 Pranam Lashkari // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -20,7 +21,7 @@ #include #include -#include +#include #include namespace boost { namespace gil { diff --git a/test/core/algorithm/extend_boundary.cpp b/test/core/algorithm/extend_boundary.cpp index 7dbe3a082a..09e524d2f3 100644 --- a/test/core/algorithm/extend_boundary.cpp +++ b/test/core/algorithm/extend_boundary.cpp @@ -6,7 +6,7 @@ // http://www.boost.org/LICENSE_1_0.txt // #include -#include +#include #include diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index 22d503eb2f..f17160d6f2 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -1,6 +1,7 @@ # Boost.GIL (Generic Image Library) - tests # # Copyright 2019 Miral Shah +# Copyright 2021 Pranam Lashkari # # Use, modification and distribution are subject to the Boost Software License, # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -21,7 +22,11 @@ foreach(_name anisotropic_diffusion hough_parameter hough_line_transform - hough_circle_transform) + hough_circle_transform + convolve + convolve_2d + convolve_cols + convolve_rows) 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..c2f3274d58 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -1,6 +1,7 @@ # Boost.GIL (Generic Image Library) - tests # # Copyright 2019 Miral Shah +# Copyright 2021 Pranam Lashkari # # Use, modification and distribution are subject to the Boost Software License, # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -23,3 +24,7 @@ run anisotropic_diffusion.cpp ; run hough_line_transform.cpp ; run hough_circle_transform.cpp ; run morphology.cpp ; +run convolve.cpp ; +run convolve_2d.cpp ; +run convolve_cols.cpp ; +run convolve_rows.cpp ; diff --git a/test/extension/numeric/convolve.cpp b/test/core/image_processing/convolve.cpp similarity index 97% rename from test/extension/numeric/convolve.cpp rename to test/core/image_processing/convolve.cpp index 7fe008ea05..c3ecb431d8 100644 --- a/test/extension/numeric/convolve.cpp +++ b/test/core/image_processing/convolve.cpp @@ -1,12 +1,13 @@ // // Copyright 2019-2020 Mateusz Loskot +// Copyright 2021 Pranam Lashkari // // Distributed under 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 diff --git a/test/extension/numeric/convolve_2d.cpp b/test/core/image_processing/convolve_2d.cpp similarity index 96% rename from test/extension/numeric/convolve_2d.cpp rename to test/core/image_processing/convolve_2d.cpp index 87efc7f6c2..3b37b8e37b 100644 --- a/test/extension/numeric/convolve_2d.cpp +++ b/test/core/image_processing/convolve_2d.cpp @@ -1,12 +1,13 @@ // // Copyright 2019 Miral Shah +// Copyright 2019-2021 Pranam Lashkari // // 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 diff --git a/test/extension/numeric/convolve_cols.cpp b/test/core/image_processing/convolve_cols.cpp similarity index 95% rename from test/extension/numeric/convolve_cols.cpp rename to test/core/image_processing/convolve_cols.cpp index c9e5576db4..c78f181b97 100644 --- a/test/extension/numeric/convolve_cols.cpp +++ b/test/core/image_processing/convolve_cols.cpp @@ -1,12 +1,13 @@ // // Copyright 2019-2020 Mateusz Loskot +// Copyright 2021 Pranam Lashkari // // Distributed under 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 diff --git a/test/extension/numeric/convolve_rows.cpp b/test/core/image_processing/convolve_rows.cpp similarity index 95% rename from test/extension/numeric/convolve_rows.cpp rename to test/core/image_processing/convolve_rows.cpp index d9b7354796..d4a99fcc8a 100644 --- a/test/extension/numeric/convolve_rows.cpp +++ b/test/core/image_processing/convolve_rows.cpp @@ -1,12 +1,13 @@ // // Copyright 2019-2020 Mateusz Loskot +// Copyright 2021 Pranam Lashkari // // Distributed under 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 diff --git a/test/extension/numeric/test_fixture.hpp b/test/core/image_processing/test_fixture.hpp similarity index 93% rename from test/extension/numeric/test_fixture.hpp rename to test/core/image_processing/test_fixture.hpp index bfd5cf6f2c..5ac90c8b6a 100644 --- a/test/extension/numeric/test_fixture.hpp +++ b/test/core/image_processing/test_fixture.hpp @@ -1,5 +1,6 @@ // // Copyright 2019 Mateusz Loskot +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at diff --git a/test/extension/numeric/CMakeLists.txt b/test/extension/numeric/CMakeLists.txt index 17dc7bd2c7..121ae76b85 100644 --- a/test/extension/numeric/CMakeLists.txt +++ b/test/extension/numeric/CMakeLists.txt @@ -1,6 +1,6 @@ # # Copyright (c) 2017 Mateusz Loskot -# Copyright (c) 2021 Pranam Lashkari +# Copyright (c) 2019-2021 Pranam Lashkari # All rights reserved. # # Distributed under the Boost Software License, Version 1.0. @@ -10,10 +10,6 @@ message(STATUS "Boost.GIL: Configuring tests in test/extension/numeric") foreach(_name - convolve - convolve_2d - convolve_cols - convolve_rows kernel kernel_fixed matrix3x2 diff --git a/test/extension/numeric/Jamfile b/test/extension/numeric/Jamfile index 175156e002..395b83d18f 100644 --- a/test/extension/numeric/Jamfile +++ b/test/extension/numeric/Jamfile @@ -14,10 +14,6 @@ alias headers : [ generate_self_contained_headers extension/numeric ] ; compile-fail kernel_1d_fixed_even_size_fail.cpp ; -run convolve.cpp ; -run convolve_2d.cpp ; -run convolve_cols.cpp ; -run convolve_rows.cpp ; run kernel.cpp ; run kernel_fixed.cpp ; run matrix3x2.cpp ; From 87a3157c4ba20b2c17d2829baa3304cf9ceb8189 Mon Sep 17 00:00:00 2001 From: Pranam Lashkari Date: Wed, 3 Mar 2021 00:52:09 +0530 Subject: [PATCH 078/193] numeric extension move into core part 4 moved numeric/pixel_numeric_operations.hpp into core --- include/boost/gil/algorithm.hpp | 3 +- .../numeric/pixel_numeric_operations.hpp | 206 +---------------- .../boost/gil/extension/numeric/sampler.hpp | 3 +- .../boost/gil/image_processing/convolve.hpp | 2 +- .../boost/gil/pixel_numeric_operations.hpp | 217 ++++++++++++++++++ test/core/pixel/CMakeLists.txt | 5 +- test/core/pixel/Jamfile | 3 + .../pixel}/pixel_numeric_operations.cpp | 4 +- .../pixel}/pixel_numeric_operations_float.cpp | 5 +- test/extension/numeric/CMakeLists.txt | 2 - test/extension/numeric/Jamfile | 2 - 11 files changed, 237 insertions(+), 215 deletions(-) create mode 100644 include/boost/gil/pixel_numeric_operations.hpp rename test/{extension/numeric => core/pixel}/pixel_numeric_operations.cpp (98%) rename test/{extension/numeric => core/pixel}/pixel_numeric_operations_float.cpp (94%) diff --git a/include/boost/gil/algorithm.hpp b/include/boost/gil/algorithm.hpp index 9b51c9c89c..e19c7a73ed 100644 --- a/include/boost/gil/algorithm.hpp +++ b/include/boost/gil/algorithm.hpp @@ -9,10 +9,9 @@ #ifndef BOOST_GIL_ALGORITHM_HPP #define BOOST_GIL_ALGORITHM_HPP -#include - #include #include +#include #include #include #include diff --git a/include/boost/gil/extension/numeric/pixel_numeric_operations.hpp b/include/boost/gil/extension/numeric/pixel_numeric_operations.hpp index c3f555d004..d3a1e3d88e 100644 --- a/include/boost/gil/extension/numeric/pixel_numeric_operations.hpp +++ b/include/boost/gil/extension/numeric/pixel_numeric_operations.hpp @@ -9,210 +9,8 @@ #ifndef BOOST_GIL_EXTENSION_NUMERIC_PIXEL_NUMERIC_OPERATIONS_HPP #define BOOST_GIL_EXTENSION_NUMERIC_PIXEL_NUMERIC_OPERATIONS_HPP -#include +#include -#include -#include - -namespace boost { namespace gil { - -// Function objects and utilities for pixel-wise numeric operations. -// -// List of currently defined functors: -// pixel_plus_t (+) -// pixel_minus_t (-) -// pixel_multiplies_scalar_t (*) -// pixel_divides_scalar_t (/) -// pixel_halves_t (/=2), -// pixel_zeros_t (=0) -// pixel_assigns_t (=) - -/// \ingroup PixelNumericOperations -/// \brief Performs channel-wise addition of two pixels. -/// \tparam PixelRef1 - models PixelConcept -/// \tparam PixelRef2 - models PixelConcept -/// \tparam PixelResult - models PixelValueConcept -template -struct pixel_plus_t -{ - auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult - { - PixelResult result; - static_transform(p1, p2, result, - channel_plus_t - < - typename channel_type::type, - typename channel_type::type, - typename channel_type::type - >()); - return result; - } -}; - -/// \ingroup PixelNumericOperations -/// \brief Performs channel-wise subtraction of two pixels. -/// \tparam PixelRef1 - models PixelConcept -/// \tparam PixelRef2 - models PixelConcept -/// \tparam PixelResult - models PixelValueConcept -template -struct pixel_minus_t -{ - auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult - { - PixelResult result; - static_transform(p1, p2, result, - channel_minus_t - < - typename channel_type::type, - typename channel_type::type, - typename channel_type::type - >()); - return result; - } -}; - -/// \ingroup PixelNumericOperations -/// \brief Performs channel-wise multiplication of pixel elements by scalar. -/// \tparam PixelRef - models PixelConcept -/// \tparam Scalar - models a scalar type -/// \tparam PixelResult - models PixelValueConcept -template -struct pixel_multiplies_scalar_t -{ - auto operator()(PixelRef const& p, Scalar const& s) const -> PixelResult - { - PixelResult result; - static_transform(p, result, - std::bind( - channel_multiplies_scalar_t::type, - Scalar, - typename channel_type::type>(), - std::placeholders::_1, s)); - return result; - } -}; - -/// \ingroup PixelNumericOperations -/// \brief Performs channel-wise multiplication of two pixels. -/// \tparam PixelRef1 - models PixelConcept -/// \tparam PixelRef1 - models PixelConcept -/// \tparam PixelResult - models PixelValueConcept -template -struct pixel_multiply_t -{ - auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult - { - PixelResult result; - static_transform(p1, p2, result, - channel_multiplies_t - < - typename channel_type::type, - typename channel_type::type, - typename channel_type::type - >()); - return result; - } -}; - -/// \ingroup PixelNumericOperations -/// \brief Performs channel-wise division of pixel elements by scalar. -/// \tparam PixelRef - models PixelConcept -/// \tparam Scalar - models a scalar type -/// \tparam PixelResult - models PixelValueConcept -template -struct pixel_divides_scalar_t -{ - auto operator()(PixelRef const& p, Scalar const& s) const -> PixelResult - { - PixelResult result; - static_transform(p, result, - std::bind(channel_divides_scalar_t::type, - Scalar, - typename channel_type::type>(), - std::placeholders::_1, s)); - return result; - } -}; - -/// \ingroup PixelNumericOperations -/// \brief Performs channel-wise division of two pixels. -/// \tparam PixelRef1 - models PixelConcept -/// \tparam PixelRef1 - models PixelConcept -/// \tparam PixelResult - models PixelValueConcept -template -struct pixel_divide_t -{ - auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult - { - PixelResult result; - static_transform(p1, p2, result, - channel_divides_t - < - typename channel_type::type, - typename channel_type::type, - typename channel_type::type - >()); - return result; - } -}; - -/// \ingroup PixelNumericOperations -/// \brief Performs channel-wise division by 2 -/// \tparam PixelRef - models PixelConcept -template -struct pixel_halves_t -{ - auto operator()(PixelRef& p) const -> PixelRef& - { - static_for_each(p, channel_halves_t::type>()); - return p; - } -}; - -/// \ingroup PixelNumericOperations -/// \brief Sets pixel elements to zero (for whatever zero means) -/// \tparam PixelRef - models PixelConcept -template -struct pixel_zeros_t -{ - auto operator()(PixelRef& p) const -> PixelRef& - { - static_for_each(p, channel_zeros_t::type>()); - return p; - } -}; - -/// \brief Sets pixel elements to zero (for whatever zero means) -/// \tparam Pixel - models PixelConcept -template -void zero_channels(Pixel& p) -{ - static_for_each(p, channel_zeros_t::type>()); -} - -/// \ingroup PixelNumericOperations -/// \brief Casts and assigns a pixel to another -/// -/// A generic implementation for casting and assigning a pixel to another. -/// User should specialize it for better performance. -/// -/// \tparam PixelRef - models PixelConcept -/// \tparam PixelResult - models PixelValueConcept -template -struct pixel_assigns_t -{ - auto operator()(PixelRef const& src, PixelResult& dst) const -> PixelResult - { - static_for_each(src, dst, - channel_assigns_t - < - typename channel_type::type, - typename channel_type::type - >()); - return dst; - } -}; - -}} // namespace boost::gil +BOOST_HEADER_DEPRECATED("") #endif diff --git a/include/boost/gil/extension/numeric/sampler.hpp b/include/boost/gil/extension/numeric/sampler.hpp index 0f71a986d7..bc7fa351a6 100644 --- a/include/boost/gil/extension/numeric/sampler.hpp +++ b/include/boost/gil/extension/numeric/sampler.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -8,8 +9,8 @@ #ifndef BOOST_GIL_EXTENSION_NUMERIC_SAMPLER_HPP #define BOOST_GIL_EXTENSION_NUMERIC_SAMPLER_HPP -#include #include +#include namespace boost { namespace gil { diff --git a/include/boost/gil/image_processing/convolve.hpp b/include/boost/gil/image_processing/convolve.hpp index 3880fc3bb1..0165aec2f3 100644 --- a/include/boost/gil/image_processing/convolve.hpp +++ b/include/boost/gil/image_processing/convolve.hpp @@ -11,11 +11,11 @@ #define BOOST_GIL_IMAGE_PROCESSING_CONVOLVE_HPP #include -#include #include #include #include +#include #include diff --git a/include/boost/gil/pixel_numeric_operations.hpp b/include/boost/gil/pixel_numeric_operations.hpp new file mode 100644 index 0000000000..b501c14e8a --- /dev/null +++ b/include/boost/gil/pixel_numeric_operations.hpp @@ -0,0 +1,217 @@ +// +// Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2021 Pranam Lashkari +// +// Distributed under 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_PIXEL_NUMERIC_OPERATIONS_HPP +#define BOOST_GIL_PIXEL_NUMERIC_OPERATIONS_HPP + +#include +#include +#include + +namespace boost { namespace gil { + +// Function objects and utilities for pixel-wise numeric operations. +// +// List of currently defined functors: +// pixel_plus_t (+) +// pixel_minus_t (-) +// pixel_multiplies_scalar_t (*) +// pixel_divides_scalar_t (/) +// pixel_halves_t (/=2), +// pixel_zeros_t (=0) +// pixel_assigns_t (=) + +/// \ingroup PixelNumericOperations +/// \brief Performs channel-wise addition of two pixels. +/// \tparam PixelRef1 - models PixelConcept +/// \tparam PixelRef2 - models PixelConcept +/// \tparam PixelResult - models PixelValueConcept +template +struct pixel_plus_t +{ + auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult + { + PixelResult result; + static_transform(p1, p2, result, + channel_plus_t + < + typename channel_type::type, + typename channel_type::type, + typename channel_type::type + >()); + return result; + } +}; + +/// \ingroup PixelNumericOperations +/// \brief Performs channel-wise subtraction of two pixels. +/// \tparam PixelRef1 - models PixelConcept +/// \tparam PixelRef2 - models PixelConcept +/// \tparam PixelResult - models PixelValueConcept +template +struct pixel_minus_t +{ + auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult + { + PixelResult result; + static_transform(p1, p2, result, + channel_minus_t + < + typename channel_type::type, + typename channel_type::type, + typename channel_type::type + >()); + return result; + } +}; + +/// \ingroup PixelNumericOperations +/// \brief Performs channel-wise multiplication of pixel elements by scalar. +/// \tparam PixelRef - models PixelConcept +/// \tparam Scalar - models a scalar type +/// \tparam PixelResult - models PixelValueConcept +template +struct pixel_multiplies_scalar_t +{ + auto operator()(PixelRef const& p, Scalar const& s) const -> PixelResult + { + PixelResult result; + static_transform(p, result, + std::bind( + channel_multiplies_scalar_t::type, + Scalar, + typename channel_type::type>(), + std::placeholders::_1, s)); + return result; + } +}; + +/// \ingroup PixelNumericOperations +/// \brief Performs channel-wise multiplication of two pixels. +/// \tparam PixelRef1 - models PixelConcept +/// \tparam PixelRef1 - models PixelConcept +/// \tparam PixelResult - models PixelValueConcept +template +struct pixel_multiply_t +{ + auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult + { + PixelResult result; + static_transform(p1, p2, result, + channel_multiplies_t + < + typename channel_type::type, + typename channel_type::type, + typename channel_type::type + >()); + return result; + } +}; + +/// \ingroup PixelNumericOperations +/// \brief Performs channel-wise division of pixel elements by scalar. +/// \tparam PixelRef - models PixelConcept +/// \tparam Scalar - models a scalar type +/// \tparam PixelResult - models PixelValueConcept +template +struct pixel_divides_scalar_t +{ + auto operator()(PixelRef const& p, Scalar const& s) const -> PixelResult + { + PixelResult result; + static_transform(p, result, + std::bind(channel_divides_scalar_t::type, + Scalar, + typename channel_type::type>(), + std::placeholders::_1, s)); + return result; + } +}; + +/// \ingroup PixelNumericOperations +/// \brief Performs channel-wise division of two pixels. +/// \tparam PixelRef1 - models PixelConcept +/// \tparam PixelRef2 - models PixelConcept +/// \tparam PixelResult - models PixelValueConcept +template +struct pixel_divide_t +{ + auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult + { + PixelResult result; + static_transform(p1, p2, result, + channel_divides_t + < + typename channel_type::type, + typename channel_type::type, + typename channel_type::type + >()); + return result; + } +}; + +/// \ingroup PixelNumericOperations +/// \brief Performs channel-wise division by 2 +/// \tparam PixelRef - models PixelConcept +template +struct pixel_halves_t +{ + auto operator()(PixelRef& p) const -> PixelRef& + { + static_for_each(p, channel_halves_t::type>()); + return p; + } +}; + +/// \ingroup PixelNumericOperations +/// \brief Sets pixel elements to zero (for whatever zero means) +/// \tparam PixelRef - models PixelConcept +template +struct pixel_zeros_t +{ + auto operator()(PixelRef& p) const -> PixelRef& + { + static_for_each(p, channel_zeros_t::type>()); + return p; + } +}; + +/// \brief Sets pixel elements to zero (for whatever zero means) +/// \tparam Pixel - models PixelConcept +template +void zero_channels(Pixel& p) +{ + static_for_each(p, channel_zeros_t::type>()); +} + +/// \ingroup PixelNumericOperations +/// \brief Casts and assigns a pixel to another +/// +/// A generic implementation for casting and assigning a pixel to another. +/// User should specialize it for better performance. +/// +/// \tparam PixelRef - models PixelConcept +/// \tparam PixelResult - models PixelValueConcept +template +struct pixel_assigns_t +{ + auto operator()(PixelRef const& src, PixelResult& dst) const -> PixelResult + { + static_for_each(src, dst, + channel_assigns_t + < + typename channel_type::type, + typename channel_type::type + >()); + return dst; + } +}; + +}} // namespace boost::gil + +#endif diff --git a/test/core/pixel/CMakeLists.txt b/test/core/pixel/CMakeLists.txt index 0cdefab254..a0d2d40713 100644 --- a/test/core/pixel/CMakeLists.txt +++ b/test/core/pixel/CMakeLists.txt @@ -1,5 +1,6 @@ # # Copyright (c) 2019 Mateusz Loskot +# Copyright (c) 2021 Pranam Lashkari # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at @@ -15,7 +16,9 @@ foreach(_name packed_pixel pixel_reference_is_mutable pixels_are_compatible - test_fixture) + test_fixture + pixel_numeric_operations + pixel_numeric_operations_float) set(_test t_core_pixel_${_name}) set(_target test_core_pixel_${_name}) diff --git a/test/core/pixel/Jamfile b/test/core/pixel/Jamfile index aa7720d0ae..b405ea5381 100644 --- a/test/core/pixel/Jamfile +++ b/test/core/pixel/Jamfile @@ -1,6 +1,7 @@ # Boost.GIL (Generic Image Library) - tests # # Copyright (c) 2019 Mateusz Loskot +# Copyright (c) 2021 Pranam Lashkari # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or @@ -19,3 +20,5 @@ compile pixels_are_compatible.cpp ; run color_convert_cmyk.cpp ; run packed_pixel.cpp ; run test_fixture.cpp ; +run pixel_numeric_operations.cpp ; +run pixel_numeric_operations_float.cpp ; diff --git a/test/extension/numeric/pixel_numeric_operations.cpp b/test/core/pixel/pixel_numeric_operations.cpp similarity index 98% rename from test/extension/numeric/pixel_numeric_operations.cpp rename to test/core/pixel/pixel_numeric_operations.cpp index 5f18ebfbbb..0851b7a94b 100644 --- a/test/extension/numeric/pixel_numeric_operations.cpp +++ b/test/core/pixel/pixel_numeric_operations.cpp @@ -1,15 +1,17 @@ // // Copyright 2019-2020 Mateusz Loskot +// Copyright 2021 Pranam Lashkari // // Distributed under 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 #include diff --git a/test/extension/numeric/pixel_numeric_operations_float.cpp b/test/core/pixel/pixel_numeric_operations_float.cpp similarity index 94% rename from test/extension/numeric/pixel_numeric_operations_float.cpp rename to test/core/pixel/pixel_numeric_operations_float.cpp index 79caa7a75d..dad46d65a1 100644 --- a/test/extension/numeric/pixel_numeric_operations_float.cpp +++ b/test/core/pixel/pixel_numeric_operations_float.cpp @@ -1,6 +1,7 @@ // // Copyright 2013 Krzysztof Czainski // Copyright 2020 Mateusz Loskot +// Copyright 2021 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -10,7 +11,9 @@ #include #include +#include #include +#include #include @@ -64,7 +67,7 @@ void test_divide() gil::rgb8_pixel_t a(10, 20, 30); gil::bgr8_pixel_t b(2, 2, 2); - gil::pixel_divide_t op; + gil::pixel_divide_t op; gil::rgb32f_pixel_t c = op(a, b); BOOST_TEST_EQ(get_color(c, gil::red_t()), 5); diff --git a/test/extension/numeric/CMakeLists.txt b/test/extension/numeric/CMakeLists.txt index 121ae76b85..33afaf5685 100644 --- a/test/extension/numeric/CMakeLists.txt +++ b/test/extension/numeric/CMakeLists.txt @@ -13,8 +13,6 @@ foreach(_name kernel kernel_fixed matrix3x2 - pixel_numeric_operations - pixel_numeric_operations_float resample) set(_test t_ext_numeric_${_name}) set(_target test_ext_numeric_${_name}) diff --git a/test/extension/numeric/Jamfile b/test/extension/numeric/Jamfile index 395b83d18f..3ce2b031f3 100644 --- a/test/extension/numeric/Jamfile +++ b/test/extension/numeric/Jamfile @@ -17,7 +17,5 @@ compile-fail kernel_1d_fixed_even_size_fail.cpp ; run kernel.cpp ; run kernel_fixed.cpp ; run matrix3x2.cpp ; -run pixel_numeric_operations.cpp ; -run pixel_numeric_operations_float.cpp ; run resample.cpp ; From 9bd8642f69bdbf5dfd01df45682efd50dd18fa53 Mon Sep 17 00:00:00 2001 From: Pranam Lashkari Date: Thu, 30 Dec 2021 10:17:37 +0530 Subject: [PATCH 079/193] numeric extension move into core part 5 moved numeric/kernel.hpp into core --- example/convolution.cpp | 2 +- example/convolve2d.cpp | 2 +- include/boost/gil/detail/math.hpp | 2 +- .../boost/gil/extension/numeric/kernel.hpp | 349 +---------------- .../boost/gil/image_processing/convolve.hpp | 2 +- include/boost/gil/image_processing/filter.hpp | 2 +- include/boost/gil/image_processing/harris.hpp | 2 +- .../boost/gil/image_processing/hessian.hpp | 2 +- include/boost/gil/image_processing/kernel.hpp | 362 ++++++++++++++++++ .../boost/gil/image_processing/morphology.hpp | 3 +- .../boost/gil/image_processing/numeric.hpp | 2 +- .../boost/gil/image_processing/threshold.hpp | 2 +- test/core/image_processing/CMakeLists.txt | 4 +- test/core/image_processing/Jamfile | 3 + .../image_processing}/kernel.cpp | 2 +- .../kernel_1d_fixed_even_size_fail.cpp | 2 +- .../image_processing}/kernel_fixed.cpp | 2 +- test/core/image_processing/test_fixture.hpp | 2 +- test/extension/numeric/CMakeLists.txt | 2 - test/extension/numeric/Jamfile | 4 +- 20 files changed, 387 insertions(+), 366 deletions(-) create mode 100644 include/boost/gil/image_processing/kernel.hpp rename test/{extension/numeric => core/image_processing}/kernel.cpp (99%) rename test/{extension/numeric => core/image_processing}/kernel_1d_fixed_even_size_fail.cpp (87%) rename test/{extension/numeric => core/image_processing}/kernel_fixed.cpp (99%) diff --git a/example/convolution.cpp b/example/convolution.cpp index 5093ccfcde..52c27b5d0f 100644 --- a/example/convolution.cpp +++ b/example/convolution.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include // Convolves the image with a Gaussian kernel. diff --git a/example/convolve2d.cpp b/example/convolve2d.cpp index e9e4422008..caa61c55e5 100644 --- a/example/convolve2d.cpp +++ b/example/convolve2d.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 934f11ac82..f7f45d2188 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -9,7 +9,7 @@ #define BOOST_GIL_IMAGE_PROCESSING_DETAIL_MATH_HPP #include -#include +#include namespace boost { namespace gil { namespace detail { diff --git a/include/boost/gil/extension/numeric/kernel.hpp b/include/boost/gil/extension/numeric/kernel.hpp index 5fc6419f0b..103df047af 100644 --- a/include/boost/gil/extension/numeric/kernel.hpp +++ b/include/boost/gil/extension/numeric/kernel.hpp @@ -1,6 +1,7 @@ // // Copyright 2005-2007 Adobe Systems Incorporated // Copyright 2019 Miral Shah +// Copyright 2022 Pranam Lashkari // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -10,352 +11,8 @@ #ifndef BOOST_GIL_EXTENSION_NUMERIC_KERNEL_HPP #define BOOST_GIL_EXTENSION_NUMERIC_KERNEL_HPP -#include -#include +#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace boost { namespace gil { - -// Definitions of 1D fixed-size and variable-size kernels and related operations - -namespace detail { - -/// \brief kernel adaptor for one-dimensional cores -/// Core needs to provide size(),begin(),end(),operator[], -/// value_type,iterator,const_iterator,reference,const_reference -template -class kernel_1d_adaptor : public Core -{ -public: - kernel_1d_adaptor() = default; - - explicit kernel_1d_adaptor(std::size_t center) - : center_(center) - { - BOOST_ASSERT(center_ < this->size()); - } - - kernel_1d_adaptor(std::size_t size, std::size_t center) - : Core(size) , center_(center) - { - BOOST_ASSERT(this->size() > 0); - BOOST_ASSERT(center_ < this->size()); // also implies `size() > 0` - } - - kernel_1d_adaptor(kernel_1d_adaptor const& other) - : Core(other), center_(other.center_) - { - BOOST_ASSERT(this->size() > 0); - BOOST_ASSERT(center_ < this->size()); // also implies `size() > 0` - } - - kernel_1d_adaptor& operator=(kernel_1d_adaptor const& other) - { - Core::operator=(other); - center_ = other.center_; - return *this; - } - - std::size_t left_size() const - { - BOOST_ASSERT(center_ < this->size()); - return center_; - } - - std::size_t right_size() const - { - BOOST_ASSERT(center_ < this->size()); - return this->size() - center_ - 1; - } - - auto center() -> std::size_t& - { - BOOST_ASSERT(center_ < this->size()); - return center_; - } - - auto center() const -> std::size_t const& - { - BOOST_ASSERT(center_ < this->size()); - return center_; - } - -private: - std::size_t center_{0}; -}; - -} // namespace detail - -/// \brief variable-size kernel -template > -class kernel_1d : public detail::kernel_1d_adaptor> -{ - using parent_t = detail::kernel_1d_adaptor>; -public: - - kernel_1d() = default; - kernel_1d(std::size_t size, std::size_t center) : parent_t(size, center) {} - - template - kernel_1d(FwdIterator elements, std::size_t size, std::size_t center) - : parent_t(size, center) - { - detail::copy_n(elements, size, this->begin()); - } - - kernel_1d(kernel_1d const& other) : parent_t(other) {} - kernel_1d& operator=(kernel_1d const& other) = default; -}; - -/// \brief static-size kernel -template -class kernel_1d_fixed : public detail::kernel_1d_adaptor> -{ - using parent_t = detail::kernel_1d_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_1d_fixed() = default; - explicit kernel_1d_fixed(std::size_t center) : parent_t(center) {} - - template - explicit kernel_1d_fixed(FwdIterator elements, std::size_t center) - : parent_t(center) - { - detail::copy_n(elements, Size, this->begin()); - } - - kernel_1d_fixed(kernel_1d_fixed const& other) : parent_t(other) {} - kernel_1d_fixed& operator=(kernel_1d_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_1d_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 detail { - -template -class kernel_2d_adaptor : public Core -{ -public: - kernel_2d_adaptor() = default; - - explicit kernel_2d_adaptor(std::size_t center_y, std::size_t center_x) - : center_(center_x, center_y) - { - BOOST_ASSERT(center_.y < this->size() && center_.x < this->size()); - } - - kernel_2d_adaptor(std::size_t size, std::size_t center_y, std::size_t center_x) - : Core(size * size), square_size(size), center_(center_x, center_y) - { - BOOST_ASSERT(this->size() > 0); - BOOST_ASSERT(center_.y < this->size() && center_.x < this->size()); // 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()); // 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_y() -> std::size_t& - { - BOOST_ASSERT(center_.y < this->size()); - return center_.y; - } - - auto center_y() const -> std::size_t const& - { - BOOST_ASSERT(center_.y < this->size()); - return center_.y; - } - - auto center_x() -> std::size_t& - { - BOOST_ASSERT(center_.x < this->size()); - return center_.x; - } - - auto center_x() 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]; - } - -protected: - std::size_t square_size{0}; - -private: - point center_{0, 0}; -}; - -/// \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 center_y, std::size_t center_x) - : parent_t(size, center_y, center_x) - {} - - template - kernel_2d(FwdIterator elements, std::size_t size, std::size_t center_y, std::size_t center_x) - : parent_t(static_cast(std::sqrt(size)), center_y, center_x) - { - 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_y, std::size_t center_x) : - parent_t(center_y, center_x) - { - this->square_size = Size; - } - - template - explicit kernel_2d_fixed(FwdIterator elements, std::size_t center_y, std::size_t center_x) - : parent_t(center_y, center_x) - { - 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; - -template -inline Kernel reverse_kernel_2d(Kernel const& kernel) -{ - Kernel result(kernel); - result.center_x() = kernel.lower_size(); - result.center_y() = kernel.right_size(); - std::reverse(result.begin(), result.end()); - return result; -} - - -/// \brief reverse a kernel_2d -template -inline kernel_2d reverse_kernel(kernel_2d const& kernel) -{ - return reverse_kernel_2d(kernel); -} - -/// \brief reverse a kernel_2d -template -inline kernel_2d_fixed reverse_kernel(kernel_2d_fixed const& kernel) -{ - return reverse_kernel_2d(kernel); -} - -} //namespace detail - -}} // namespace boost::gil +BOOST_HEADER_DEPRECATED("") #endif diff --git a/include/boost/gil/image_processing/convolve.hpp b/include/boost/gil/image_processing/convolve.hpp index 0165aec2f3..a6369c89a6 100644 --- a/include/boost/gil/image_processing/convolve.hpp +++ b/include/boost/gil/image_processing/convolve.hpp @@ -10,7 +10,7 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_CONVOLVE_HPP #define BOOST_GIL_IMAGE_PROCESSING_CONVOLVE_HPP -#include +#include #include #include diff --git a/include/boost/gil/image_processing/filter.hpp b/include/boost/gil/image_processing/filter.hpp index 097a85f1c7..574c4cde5a 100644 --- a/include/boost/gil/image_processing/filter.hpp +++ b/include/boost/gil/image_processing/filter.hpp @@ -10,7 +10,7 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_FILTER_HPP #define BOOST_GIL_IMAGE_PROCESSING_FILTER_HPP -#include +#include #include diff --git a/include/boost/gil/image_processing/harris.hpp b/include/boost/gil/image_processing/harris.hpp index 18185afd81..56e0cdb30e 100644 --- a/include/boost/gil/image_processing/harris.hpp +++ b/include/boost/gil/image_processing/harris.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include namespace boost { namespace gil { /// \defgroup CornerDetectionAlgorithms diff --git a/include/boost/gil/image_processing/hessian.hpp b/include/boost/gil/image_processing/hessian.hpp index f37c0ffdd5..4d037e26a0 100644 --- a/include/boost/gil/image_processing/hessian.hpp +++ b/include/boost/gil/image_processing/hessian.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include namespace boost { namespace gil { diff --git a/include/boost/gil/image_processing/kernel.hpp b/include/boost/gil/image_processing/kernel.hpp new file mode 100644 index 0000000000..98682d2bb3 --- /dev/null +++ b/include/boost/gil/image_processing/kernel.hpp @@ -0,0 +1,362 @@ +// +// Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2019 Miral Shah +// Copyright 2022 Pranam Lashkari +// +// Distributed under 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_KERNEL_HPP +#define BOOST_GIL_IMAGE_PROCESSING_KERNEL_HPP + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace gil { + +// Definitions of 1D fixed-size and variable-size kernels and related operations + +namespace detail { + +/// \brief kernel adaptor for one-dimensional cores +/// Core needs to provide size(),begin(),end(),operator[], +/// value_type,iterator,const_iterator,reference,const_reference +template +class kernel_1d_adaptor : public Core +{ +public: + kernel_1d_adaptor() = default; + + explicit kernel_1d_adaptor(std::size_t center) + : center_(center) + { + BOOST_ASSERT(center_ < this->size()); + } + + kernel_1d_adaptor(std::size_t size, std::size_t center) + : Core(size) , center_(center) + { + BOOST_ASSERT(this->size() > 0); + BOOST_ASSERT(center_ < this->size()); // also implies `size() > 0` + } + + kernel_1d_adaptor(kernel_1d_adaptor const& other) + : Core(other), center_(other.center_) + { + BOOST_ASSERT(this->size() > 0); + BOOST_ASSERT(center_ < this->size()); // also implies `size() > 0` + } + + kernel_1d_adaptor& operator=(kernel_1d_adaptor const& other) + { + Core::operator=(other); + center_ = other.center_; + return *this; + } + + std::size_t left_size() const + { + BOOST_ASSERT(center_ < this->size()); + return center_; + } + + std::size_t right_size() const + { + BOOST_ASSERT(center_ < this->size()); + return this->size() - center_ - 1; + } + + auto center() -> std::size_t& + { + BOOST_ASSERT(center_ < this->size()); + return center_; + } + + auto center() const -> std::size_t const& + { + BOOST_ASSERT(center_ < this->size()); + return center_; + } + +private: + std::size_t center_{0}; +}; + +} // namespace detail + +/// \brief variable-size kernel +template > +class kernel_1d : public detail::kernel_1d_adaptor> +{ + using parent_t = detail::kernel_1d_adaptor>; +public: + + kernel_1d() = default; + kernel_1d(std::size_t size, std::size_t center) : parent_t(size, center) {} + + template + kernel_1d(FwdIterator elements, std::size_t size, std::size_t center) + : parent_t(size, center) + { + detail::copy_n(elements, size, this->begin()); + } + + kernel_1d(kernel_1d const& other) : parent_t(other) {} + kernel_1d& operator=(kernel_1d const& other) = default; +}; + +/// \brief static-size kernel +template +class kernel_1d_fixed : public detail::kernel_1d_adaptor> +{ + using parent_t = detail::kernel_1d_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_1d_fixed() = default; + explicit kernel_1d_fixed(std::size_t center) : parent_t(center) {} + + template + explicit kernel_1d_fixed(FwdIterator elements, std::size_t center) + : parent_t(center) + { + detail::copy_n(elements, Size, this->begin()); + } + + kernel_1d_fixed(kernel_1d_fixed const& other) : parent_t(other) {} + kernel_1d_fixed& operator=(kernel_1d_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_1d_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 detail { + +template +class kernel_2d_adaptor : public Core +{ +public: + kernel_2d_adaptor() = default; + + explicit kernel_2d_adaptor(std::size_t center_y, std::size_t center_x) + : center_(center_x, center_y) + { + BOOST_ASSERT(center_.y < this->size() && center_.x < this->size()); + } + + kernel_2d_adaptor(std::size_t size, std::size_t center_y, std::size_t center_x) + : Core(size * size), square_size(size), center_(center_x, center_y) + { + BOOST_ASSERT(this->size() > 0); + BOOST_ASSERT(center_.y < this->size() && center_.x < this->size()); // 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()); // 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_y() -> std::size_t& + { + BOOST_ASSERT(center_.y < this->size()); + return center_.y; + } + + auto center_y() const -> std::size_t const& + { + BOOST_ASSERT(center_.y < this->size()); + return center_.y; + } + + auto center_x() -> std::size_t& + { + BOOST_ASSERT(center_.x < this->size()); + return center_.x; + } + + auto center_x() 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]; + } + +protected: + std::size_t square_size{0}; + +private: + point center_{0, 0}; +}; + +/// \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 center_y, std::size_t center_x) + : parent_t(size, center_y, center_x) + {} + + template + kernel_2d(FwdIterator elements, std::size_t size, std::size_t center_y, std::size_t center_x) + : parent_t(static_cast(std::sqrt(size)), center_y, center_x) + { + 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_y, std::size_t center_x) : + parent_t(center_y, center_x) + { + this->square_size = Size; + } + + template + explicit kernel_2d_fixed(FwdIterator elements, std::size_t center_y, std::size_t center_x) + : parent_t(center_y, center_x) + { + 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; + +template +inline Kernel reverse_kernel_2d(Kernel const& kernel) +{ + Kernel result(kernel); + result.center_x() = kernel.lower_size(); + result.center_y() = kernel.right_size(); + std::reverse(result.begin(), result.end()); + return result; +} + + +/// \brief reverse a kernel_2d +template +inline kernel_2d reverse_kernel(kernel_2d const& kernel) +{ + return reverse_kernel_2d(kernel); +} + +/// \brief reverse a kernel_2d +template +inline kernel_2d_fixed reverse_kernel(kernel_2d_fixed const& kernel) +{ + return reverse_kernel_2d(kernel); +} + +} //namespace detail + +}} // namespace boost::gil + +#endif diff --git a/include/boost/gil/image_processing/morphology.hpp b/include/boost/gil/image_processing/morphology.hpp index 1149b2c165..e2e1926ab4 100644 --- a/include/boost/gil/image_processing/morphology.hpp +++ b/include/boost/gil/image_processing/morphology.hpp @@ -8,7 +8,8 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP #define BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP -#include + +#include #include #include diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index 6241c8490b..3ac989ccb9 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -9,7 +9,7 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_NUMERIC_HPP #define BOOST_GIL_IMAGE_PROCESSING_NUMERIC_HPP -#include +#include #include #include #include diff --git a/include/boost/gil/image_processing/threshold.hpp b/include/boost/gil/image_processing/threshold.hpp index 78809f6d38..b3b56356fc 100644 --- a/include/boost/gil/image_processing/threshold.hpp +++ b/include/boost/gil/image_processing/threshold.hpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index f17160d6f2..680c41047c 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -26,7 +26,9 @@ foreach(_name convolve convolve_2d convolve_cols - convolve_rows) + convolve_rows + kernel + kernel_fixed) 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 c2f3274d58..6f4d77f76c 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -10,6 +10,7 @@ import testing ; compile-fail threshold_color_spaces_not_compatible_fail.cpp ; +compile-fail kernel_1d_fixed_even_size_fail.cpp ; run threshold_binary.cpp ; run threshold_truncate.cpp ; run threshold_otsu.cpp ; @@ -28,3 +29,5 @@ run convolve.cpp ; run convolve_2d.cpp ; run convolve_cols.cpp ; run convolve_rows.cpp ; +run kernel.cpp ; +run kernel_fixed.cpp ; diff --git a/test/extension/numeric/kernel.cpp b/test/core/image_processing/kernel.cpp similarity index 99% rename from test/extension/numeric/kernel.cpp rename to test/core/image_processing/kernel.cpp index a75f1cef83..781a74fa22 100644 --- a/test/extension/numeric/kernel.cpp +++ b/test/core/image_processing/kernel.cpp @@ -8,7 +8,7 @@ // #define BOOST_DISABLE_ASSERTS 1 // kernel_1d_adaptor assertions are too strict #include -#include +#include #include diff --git a/test/extension/numeric/kernel_1d_fixed_even_size_fail.cpp b/test/core/image_processing/kernel_1d_fixed_even_size_fail.cpp similarity index 87% rename from test/extension/numeric/kernel_1d_fixed_even_size_fail.cpp rename to test/core/image_processing/kernel_1d_fixed_even_size_fail.cpp index a8be85fcd7..9f78519b7f 100644 --- a/test/extension/numeric/kernel_1d_fixed_even_size_fail.cpp +++ b/test/core/image_processing/kernel_1d_fixed_even_size_fail.cpp @@ -5,7 +5,7 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#include +#include namespace gil = boost::gil; diff --git a/test/extension/numeric/kernel_fixed.cpp b/test/core/image_processing/kernel_fixed.cpp similarity index 99% rename from test/extension/numeric/kernel_fixed.cpp rename to test/core/image_processing/kernel_fixed.cpp index cfd075eafc..3f10246da1 100644 --- a/test/extension/numeric/kernel_fixed.cpp +++ b/test/core/image_processing/kernel_fixed.cpp @@ -8,7 +8,7 @@ // #define BOOST_DISABLE_ASSERTS 1 // kernel_1d_adaptor assertions are too strict #include -#include +#include #include diff --git a/test/core/image_processing/test_fixture.hpp b/test/core/image_processing/test_fixture.hpp index 5ac90c8b6a..b49fb6cd76 100644 --- a/test/core/image_processing/test_fixture.hpp +++ b/test/core/image_processing/test_fixture.hpp @@ -7,7 +7,7 @@ // http://www.boost.org/LICENSE_1_0.txt // #include -#include +#include #include #include diff --git a/test/extension/numeric/CMakeLists.txt b/test/extension/numeric/CMakeLists.txt index 33afaf5685..dccc0cc5c7 100644 --- a/test/extension/numeric/CMakeLists.txt +++ b/test/extension/numeric/CMakeLists.txt @@ -10,8 +10,6 @@ message(STATUS "Boost.GIL: Configuring tests in test/extension/numeric") foreach(_name - kernel - kernel_fixed matrix3x2 resample) set(_test t_ext_numeric_${_name}) diff --git a/test/extension/numeric/Jamfile b/test/extension/numeric/Jamfile index 3ce2b031f3..d538e7ab26 100644 --- a/test/extension/numeric/Jamfile +++ b/test/extension/numeric/Jamfile @@ -12,10 +12,8 @@ import testing ; alias headers : [ generate_self_contained_headers extension/numeric ] ; -compile-fail kernel_1d_fixed_even_size_fail.cpp ; -run kernel.cpp ; -run kernel_fixed.cpp ; + run matrix3x2.cpp ; run resample.cpp ; From f4c70a86065374eddc4455725c0013f22de4e70a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 22 Feb 2022 19:18:55 +0100 Subject: [PATCH 080/193] Add support for to IO (#636) If neither nor is detected, then require . If user defines BOOST_GIL_IO_USE_BOOST_FILESYSTEM macro, then is pre-selected and required, and search for any of the C++ standard implementation is skipped. Remove end-user macro BOOST_GIL_IO_ADD_FS_PATH_SUPPORT Require tests to always build with support of either detected C++ filesystem or pre-selected Boost.Filesystem. Closes ##222 --- doc/io.rst | 15 +++-- include/boost/gil/io/detail/filesystem.hpp | 65 +++++++++++++++++++ include/boost/gil/io/make_backend.hpp | 4 +- .../gil/io/make_dynamic_image_reader.hpp | 8 +-- .../gil/io/make_dynamic_image_writer.hpp | 4 +- include/boost/gil/io/make_reader.hpp | 8 +-- include/boost/gil/io/make_scanline_reader.hpp | 4 +- include/boost/gil/io/make_writer.hpp | 8 +-- include/boost/gil/io/path_spec.hpp | 49 ++------------ test/extension/io/bmp/bmp_make.cpp | 4 +- test/extension/io/bmp/bmp_test.cpp | 4 +- test/extension/io/jpeg/jpeg_test.cpp | 4 +- test/extension/io/paths.hpp | 24 +------ .../extension/io/png/png_file_format_test.cpp | 4 +- test/extension/io/png/png_read_test.cpp | 4 +- test/extension/io/png/png_write_test.cpp | 2 - test/extension/io/raw/raw_test.cpp | 4 +- test/extension/io/simple_all_formats.cpp | 2 +- test/extension/io/targa/targa_test.cpp | 3 +- test/extension/io/tiff/tiff_test.cpp | 4 +- 20 files changed, 100 insertions(+), 124 deletions(-) create mode 100644 include/boost/gil/io/detail/filesystem.hpp diff --git a/doc/io.rst b/doc/io.rst index 29736e9ef1..01462dbc97 100644 --- a/doc/io.rst +++ b/doc/io.rst @@ -205,9 +205,10 @@ If that's the case the user has to use the xxx_and_convert_xxx variants. All functions take the filename or a device as the first parameter. The filename can be anything from a C-string, ``std::string``, -``std::wstring`` and ``boost::filesystem`` path. When using the path -object the user needs to define the ADD_FS_PATH_SUPPORT compiler symbol to -include the boost::filesystem dependency. +``std::wstring`` to ``std::filesystem`` and ``boost::filesystem`` path. +The availability of the ``std::filesystem`` is detected automatically, +unless ``BOOST_GIL_IO_USE_BOOST_FILESYSTEM`` macro is defined that forces +preference of the Boost.Filesystem. Devices could be a ``FILE*``, ``std::ifstream``, and ``TIFF*`` for TIFF images. The second parameter is either an image or view type depending on the @@ -303,9 +304,10 @@ Write Interface There is only one function for writing out images, write_view. Similar to reading the first parameter is either a filename or a device. The filename can be anything from a C-string, ``std::string``, -``std::wstring``, and ``boost::filesystem`` path. When using the path object -the user needs to define the ``ADD_FS_PATH_SUPPORT`` compiler symbol to -include the ``boost::filesystem`` dependency. +``std::wstring`` to ``std::filesystem`` and ``boost::filesystem`` path. +The availability of the ``std::filesystem`` is detected automatically, +unless ``BOOST_GIL_IO_USE_BOOST_FILESYSTEM`` macro is defined that forces +preference of the Boost.Filesystem. Devices could be ``FILE*``, ``std::ifstream``, and ``TIFF*`` for TIFF images. The second parameter is an view object to image being written. @@ -342,7 +344,6 @@ that can be set by the user: Symbol Description ======================================================== ======================================================== BOOST_GIL_IO_ENABLE_GRAY_ALPHA Enable the color space "gray_alpha". -BOOST_GIL_IO_ADD_FS_PATH_SUPPORT Enable boost::filesystem 3.0 library. BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED Use libpng in floating point mode. This symbol is incompatible with BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED. BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED Use libpng in integer mode. This symbol is incompatible with BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED. BOOST_GIL_IO_PNG_DITHERING_SUPPORTED Look up "dithering" in libpng manual for explanation. diff --git a/include/boost/gil/io/detail/filesystem.hpp b/include/boost/gil/io/detail/filesystem.hpp new file mode 100644 index 0000000000..2e4c702a75 --- /dev/null +++ b/include/boost/gil/io/detail/filesystem.hpp @@ -0,0 +1,65 @@ +// +// Copyright 2022 Mateusz Loskot +// +// Distributed under 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_IO_DETAIL_FILESYSTEM_HPP +#define BOOST_GIL_IO_DETAIL_FILESYSTEM_HPP + +#include + +#if !defined(BOOST_GIL_IO_USE_BOOST_FILESYSTEM) +#if !defined(BOOST_NO_CXX17_HDR_FILESYSTEM) || defined(__cpp_lib_filesystem) +#include +#define BOOST_GIL_IO_USE_STD_FILESYSTEM +#elif defined(__cpp_lib_experimental_filesystem) +#include +#define BOOST_GIL_IO_USE_STD_FILESYSTEM +#define BOOST_GIL_IO_USE_STD_EXPERIMENTAL_FILESYSTEM +#endif +#endif // !BOOST_GIL_IO_USE_BOOST_FILESYSTEM + +#if !defined(BOOST_GIL_IO_USE_STD_FILESYSTEM) +// Disable warning: conversion to 'std::atomic::__integral_type {aka int}' from 'long int' may alter its value +#if defined(BOOST_CLANG) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshorten-64-to-32" +#endif + +#if defined(BOOST_GCC) && (BOOST_GCC >= 40900) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#endif + +#define BOOST_FILESYSTEM_VERSION 3 +#include +#define BOOST_GIL_IO_USE_BOOST_FILESYSTEM + +#if defined(BOOST_CLANG) +#pragma clang diagnostic pop +#endif + +#if defined(BOOST_GCC) && (BOOST_GCC >= 40900) +#pragma GCC diagnostic pop +#endif + +#endif + +namespace boost { namespace gil { namespace detail { + +#if defined(BOOST_GIL_IO_USE_STD_EXPERIMENTAL_FILESYSTEM) +namespace filesystem = std::experimental::filesystem; +#elif defined(BOOST_GIL_IO_USE_STD_FILESYSTEM) +namespace filesystem = std::filesystem; +#else +#if !defined(BOOST_GIL_IO_USE_BOOST_FILESYSTEM) +#error "Boost.Filesystem is required if C++17 is not available" +#endif +namespace filesystem = boost::filesystem; +#endif + +}}} // namespace boost::gil::detail + +#endif \ No newline at end of file diff --git a/include/boost/gil/io/make_backend.hpp b/include/boost/gil/io/make_backend.hpp index 54a518e6ca..b3fe40d6be 100644 --- a/include/boost/gil/io/make_backend.hpp +++ b/include/boost/gil/io/make_backend.hpp @@ -55,17 +55,15 @@ auto make_reader_backend( return reader_backend(device, settings); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline auto make_reader_backend( - filesystem::path const& path, + detail::filesystem::path const& path, image_read_settings const& settings) -> typename get_reader_backend::type { return make_reader_backend(path.wstring(), settings); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline diff --git a/include/boost/gil/io/make_dynamic_image_reader.hpp b/include/boost/gil/io/make_dynamic_image_reader.hpp index 88006983bf..e93ea1927e 100644 --- a/include/boost/gil/io/make_dynamic_image_reader.hpp +++ b/include/boost/gil/io/make_dynamic_image_reader.hpp @@ -54,16 +54,14 @@ auto make_dynamic_image_reader( typename get_dynamic_image_reader::type(device, settings); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline auto make_dynamic_image_reader( - filesystem::path const& path, image_read_settings const& settings) + detail::filesystem::path const& path, image_read_settings const& settings) -> typename get_dynamic_image_reader::type { return make_dynamic_image_reader(path.wstring(), settings); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline @@ -109,15 +107,13 @@ auto make_dynamic_image_reader(std::wstring const& file_name, FormatTag const&) return make_dynamic_image_reader(file_name, image_read_settings()); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline -auto make_dynamic_image_reader(filesystem::path const& path, FormatTag const&) +auto make_dynamic_image_reader(detail::filesystem::path const& path, FormatTag const&) -> typename get_dynamic_image_reader::type { return make_dynamic_image_reader(path.wstring(), image_read_settings()); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline diff --git a/include/boost/gil/io/make_dynamic_image_writer.hpp b/include/boost/gil/io/make_dynamic_image_writer.hpp index 978c7165eb..2ef5f3e95d 100644 --- a/include/boost/gil/io/make_dynamic_image_writer.hpp +++ b/include/boost/gil/io/make_dynamic_image_writer.hpp @@ -69,8 +69,8 @@ inline typename get_dynamic_image_writer< std::wstring , FormatTag >::type -make_dynamic_image_writer( const filesystem::path& path - , const image_write_info< FormatTag >& info +make_dynamic_image_writer( detail::filesystem::path const& path + , image_write_info const& info ) { return make_dynamic_image_writer( path.wstring() diff --git a/include/boost/gil/io/make_reader.hpp b/include/boost/gil/io/make_reader.hpp index 25b3d6707c..6a0ca9479b 100644 --- a/include/boost/gil/io/make_reader.hpp +++ b/include/boost/gil/io/make_reader.hpp @@ -70,7 +70,6 @@ make_reader( const std::wstring& file_name ); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template< typename FormatTag , typename ConversionPolicy > @@ -79,7 +78,7 @@ typename get_reader< std::wstring , FormatTag , ConversionPolicy >::type -make_reader( const filesystem::path& path +make_reader( detail::filesystem::path const& path , const image_read_settings< FormatTag >& settings , const ConversionPolicy& cc ) @@ -89,7 +88,6 @@ make_reader( const filesystem::path& path , cc ); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline @@ -153,7 +151,6 @@ make_reader( const std::wstring& file_name ); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template< typename FormatTag , typename ConversionPolicy > @@ -162,7 +159,7 @@ typename get_reader< std::wstring , FormatTag , ConversionPolicy >::type -make_reader( const filesystem::path& path +make_reader( detail::filesystem::path const& path , const FormatTag& , const ConversionPolicy& cc ) @@ -172,7 +169,6 @@ make_reader( const filesystem::path& path , cc ); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline diff --git a/include/boost/gil/io/make_scanline_reader.hpp b/include/boost/gil/io/make_scanline_reader.hpp index 4ca01a72b0..73e45a54c6 100644 --- a/include/boost/gil/io/make_scanline_reader.hpp +++ b/include/boost/gil/io/make_scanline_reader.hpp @@ -63,13 +63,12 @@ make_scanline_reader( const std::wstring& file_name ); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template< typename FormatTag > inline typename get_scanline_reader< std::wstring , FormatTag >::type -make_scanline_reader( const filesystem::path& path +make_scanline_reader( detail::filesystem::path const& path , FormatTag const& ) { @@ -77,7 +76,6 @@ make_scanline_reader( const filesystem::path& path , image_read_settings< FormatTag >() ); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline diff --git a/include/boost/gil/io/make_writer.hpp b/include/boost/gil/io/make_writer.hpp index 8bad4b7164..69025d8778 100644 --- a/include/boost/gil/io/make_writer.hpp +++ b/include/boost/gil/io/make_writer.hpp @@ -60,13 +60,12 @@ make_writer( const std::wstring& file_name ); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template< typename FormatTag > inline typename get_writer< std::wstring , FormatTag >::type -make_writer( const filesystem::path& path +make_writer( detail::filesystem::path const& path , const image_write_info< FormatTag >& info ) { @@ -74,7 +73,6 @@ make_writer( const filesystem::path& path , info ); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline @@ -125,13 +123,12 @@ make_writer( const std::wstring& file_name ); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template< typename FormatTag > inline typename get_writer< std::wstring , FormatTag >::type -make_writer( const filesystem::path& path +make_writer( detail::filesystem::path const& path , const FormatTag& tag ) { @@ -139,7 +136,6 @@ make_writer( const filesystem::path& path , tag ); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline diff --git a/include/boost/gil/io/path_spec.hpp b/include/boost/gil/io/path_spec.hpp index dcb2a85230..8ffbc2129a 100644 --- a/include/boost/gil/io/path_spec.hpp +++ b/include/boost/gil/io/path_spec.hpp @@ -8,29 +8,7 @@ #ifndef BOOST_GIL_IO_PATH_SPEC_HPP #define BOOST_GIL_IO_PATH_SPEC_HPP -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT -// Disable warning: conversion to 'std::atomic::__integral_type {aka int}' from 'long int' may alter its value -#if defined(BOOST_CLANG) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wshorten-64-to-32" -#endif - -#if defined(BOOST_GCC) && (BOOST_GCC >= 40900) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - -#define BOOST_FILESYSTEM_VERSION 3 -#include - -#if defined(BOOST_CLANG) -#pragma clang diagnostic pop -#endif - -#if defined(BOOST_GCC) && (BOOST_GCC >= 40900) -#pragma GCC diagnostic pop -#endif -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT +#include #include #include @@ -53,15 +31,8 @@ template struct is_supported_path_spec : std::true_ template struct is_supported_path_spec : std::true_type {}; template struct is_supported_path_spec : std::true_type {}; -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT -template<> struct is_supported_path_spec< filesystem::path > : std::true_type {}; -template<> struct is_supported_path_spec< const filesystem::path > : std::true_type {}; -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT - - -/// -/// convert_to_string -/// +template<> struct is_supported_path_spec : std::true_type {}; +template<> struct is_supported_path_spec : std::true_type {}; inline std::string convert_to_string( std::string const& obj) { @@ -87,16 +58,10 @@ inline std::string convert_to_string( char* str ) return std::string( str ); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT -inline std::string convert_to_string( const filesystem::path& path ) +inline std::string convert_to_string(filesystem::path const& path) { - return convert_to_string( path.string() ); + return convert_to_string(path.string()); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT - -/// -/// convert_to_native_string -/// inline const char* convert_to_native_string( char* str ) { @@ -131,8 +96,6 @@ inline const char* convert_to_native_string( const std::wstring& str ) return c; } -} // namespace detail -} // namespace gil -} // namespace boost +}}} // namespace boost::gil::detail #endif diff --git a/test/extension/io/bmp/bmp_make.cpp b/test/extension/io/bmp/bmp_make.cpp index 26be077261..1746264398 100644 --- a/test/extension/io/bmp/bmp_make.cpp +++ b/test/extension/io/bmp/bmp_make.cpp @@ -5,8 +5,6 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT -#define BOOST_FILESYSTEM_VERSION 3 #include #include @@ -22,7 +20,7 @@ #include "paths.hpp" -namespace fs = boost::filesystem; +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; void test_make_reader_backend() diff --git a/test/extension/io/bmp/bmp_test.cpp b/test/extension/io/bmp/bmp_test.cpp index 811a7491bc..8f18f3e66a 100644 --- a/test/extension/io/bmp/bmp_test.cpp +++ b/test/extension/io/bmp/bmp_test.cpp @@ -5,8 +5,6 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT -#define BOOST_FILESYSTEM_VERSION 3 #include #include @@ -21,7 +19,7 @@ #include "paths.hpp" #include "subimage_test.hpp" -namespace fs = boost::filesystem; +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; namespace mp11 = boost::mp11; diff --git a/test/extension/io/jpeg/jpeg_test.cpp b/test/extension/io/jpeg/jpeg_test.cpp index 42bb6a3fd9..b961bd19a9 100644 --- a/test/extension/io/jpeg/jpeg_test.cpp +++ b/test/extension/io/jpeg/jpeg_test.cpp @@ -5,8 +5,6 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_FILESYSTEM_VERSION 3 -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT #include #include @@ -21,7 +19,7 @@ #include "paths.hpp" #include "subimage_test.hpp" -namespace fs = boost::filesystem; +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; namespace mp11 = boost::mp11; diff --git a/test/extension/io/paths.hpp b/test/extension/io/paths.hpp index 9726afcccf..cf983240d4 100644 --- a/test/extension/io/paths.hpp +++ b/test/extension/io/paths.hpp @@ -8,33 +8,13 @@ #ifndef BOOST_GIL_TEST_EXTENSION_IO_PATHS_HPP #define BOOST_GIL_TEST_EXTENSION_IO_PATHS_HPP -// Disable warning: conversion to 'std::atomic::__integral_type {aka int}' from 'long int' may alter its value -#if defined(BOOST_CLANG) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wshorten-64-to-32" -#endif - -#if defined(BOOST_GCC) && (BOOST_GCC >= 40900) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - -#define BOOST_FILESYSTEM_VERSION 3 -#include - -#if defined(BOOST_CLANG) -#pragma clang diagnostic pop -#endif - -#if defined(BOOST_GCC) && (BOOST_GCC >= 40900) -#pragma GCC diagnostic pop -#endif +#include #include // `base` holds the path to ../.., i.e. the directory containing `images` static const std::string base = - (boost::filesystem::absolute(boost::filesystem::path(__FILE__)).parent_path().string()) + "/"; + (boost::gil::detail::filesystem::absolute(boost::gil::detail::filesystem::path(__FILE__)).parent_path().string()) + "/"; static const std::string bmp_in = base + "images/bmp/"; static const std::string bmp_out = base + "output/"; diff --git a/test/extension/io/png/png_file_format_test.cpp b/test/extension/io/png/png_file_format_test.cpp index cda7fffdeb..f5713ca094 100644 --- a/test/extension/io/png/png_file_format_test.cpp +++ b/test/extension/io/png/png_file_format_test.cpp @@ -5,9 +5,7 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT #define BOOST_GIL_IO_ENABLE_GRAY_ALPHA -#define BOOST_FILESYSTEM_VERSION 3 #include #include @@ -15,8 +13,8 @@ #include "paths.hpp" +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; -namespace fs = boost::filesystem; #ifdef BOOST_GIL_IO_USE_PNG_TEST_SUITE_IMAGES diff --git a/test/extension/io/png/png_read_test.cpp b/test/extension/io/png/png_read_test.cpp index 5b51cf5480..0217aa9d67 100644 --- a/test/extension/io/png/png_read_test.cpp +++ b/test/extension/io/png/png_read_test.cpp @@ -5,9 +5,7 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT #define BOOST_GIL_IO_ENABLE_GRAY_ALPHA -#define BOOST_FILESYSTEM_VERSION 3 #include #include @@ -22,8 +20,8 @@ #include "scanline_read_test.hpp" #include "test_utility_output_stream.hpp" +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; -namespace fs = boost::filesystem; using gray_alpha8_pixel_t = gil::pixel; using gray_alpha8c_pixel_t = gil::pixel const; diff --git a/test/extension/io/png/png_write_test.cpp b/test/extension/io/png/png_write_test.cpp index 85f1541afa..da4e5d66e8 100644 --- a/test/extension/io/png/png_write_test.cpp +++ b/test/extension/io/png/png_write_test.cpp @@ -5,9 +5,7 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT #define BOOST_GIL_IO_ENABLE_GRAY_ALPHA -#define BOOST_FILESYSTEM_VERSION 3 #include #include diff --git a/test/extension/io/raw/raw_test.cpp b/test/extension/io/raw/raw_test.cpp index 810ebf0b91..71ce6037d4 100644 --- a/test/extension/io/raw/raw_test.cpp +++ b/test/extension/io/raw/raw_test.cpp @@ -5,8 +5,6 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_FILESYSTEM_VERSION 3 -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT #include #include @@ -19,7 +17,7 @@ #include "paths.hpp" #include "subimage_test.hpp" -namespace fs = boost::filesystem; +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; namespace mp11 = boost::mp11; diff --git a/test/extension/io/simple_all_formats.cpp b/test/extension/io/simple_all_formats.cpp index 2acdb39fe4..b4bc87adca 100644 --- a/test/extension/io/simple_all_formats.cpp +++ b/test/extension/io/simple_all_formats.cpp @@ -16,8 +16,8 @@ #include "paths.hpp" +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; -namespace fs = boost::filesystem; // Test will include all format's headers and load and write some images. // This test is more of a compilation test. diff --git a/test/extension/io/targa/targa_test.cpp b/test/extension/io/targa/targa_test.cpp index c94e102244..accc1f7c6a 100644 --- a/test/extension/io/targa/targa_test.cpp +++ b/test/extension/io/targa/targa_test.cpp @@ -5,7 +5,6 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT #include #include @@ -21,7 +20,7 @@ #include "subimage_test.hpp" #include "test_utility_output_stream.hpp" -namespace fs = boost::filesystem; +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; namespace mp11 = boost::mp11; diff --git a/test/extension/io/tiff/tiff_test.cpp b/test/extension/io/tiff/tiff_test.cpp index f8ee2f0b09..f62492d1ad 100644 --- a/test/extension/io/tiff/tiff_test.cpp +++ b/test/extension/io/tiff/tiff_test.cpp @@ -5,8 +5,6 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#define BOOST_FILESYSTEM_VERSION 3 -#define BOOST_GIL_IO_ADD_FS_PATH_SUPPORT #include #include @@ -21,7 +19,7 @@ #include "paths.hpp" #include "subimage_test.hpp" -namespace fs = boost::filesystem; +namespace fs = boost::gil::detail::filesystem; namespace gil = boost::gil; namespace mp11 = boost::mp11; From 4943e403bcd3181209bd1b94e1a63fdd04e48027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 28 Mar 2022 22:39:44 +0200 Subject: [PATCH 081/193] chore: Bump CMake version in CMakeLists.txt --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0f777bd5b..5de286f35d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2019 Mateusz Loskot +# Copyright (c) 2017-2022 Mateusz Loskot # Copyright (c) 2020-2021 Peter Dimov # # Distributed under the Boost Software License, Version 1.0. @@ -11,7 +11,7 @@ if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) # Generated by `boostdep --cmake gil` -cmake_minimum_required(VERSION 3.8...3.20) +cmake_minimum_required(VERSION 3.8...3.23) project(boost_gil VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) From 63f5837bd35a3c78bc0952035f02126306f35390 Mon Sep 17 00:00:00 2001 From: Nicolas Herry Date: Wed, 6 Apr 2022 11:42:56 +0200 Subject: [PATCH 082/193] Removed deprecated.hpp (#627) Closes #625 too --- include/boost/gil.hpp | 1 - include/boost/gil/deprecated.hpp | 65 -------------------------------- 2 files changed, 66 deletions(-) delete mode 100644 include/boost/gil/deprecated.hpp diff --git a/include/boost/gil.hpp b/include/boost/gil.hpp index 0df6b59fb5..087c9fe8de 100644 --- a/include/boost/gil.hpp +++ b/include/boost/gil.hpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/include/boost/gil/deprecated.hpp b/include/boost/gil/deprecated.hpp deleted file mode 100644 index 77459d2c72..0000000000 --- a/include/boost/gil/deprecated.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright 2005-2007 Adobe Systems Incorporated -// -// Distributed under 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_DEPRECATED_HPP -#define BOOST_GIL_DEPRECATED_HPP - -#include - -/// This file is provided as a courtesy to ease upgrading GIL client code. -/// Please make sure your code compiles when this file is not included. - -#define planar_ptr planar_pixel_iterator -#define planar_ref planar_pixel_reference -#define membased_2d_locator memory_based_2d_locator -#define pixel_step_iterator memory_based_step_iterator -#define pixel_image_iterator iterator_from_2d - -#define equal_channels static_equal -#define copy_channels static_copy -#define fill_channels static_fill -#define generate_channels static_generate -#define for_each_channel static_for_each -#define transform_channels static_transform -#define max_channel static_max -#define min_channel static_min - -#define semantic_channel semantic_at_c - -template -void resize_clobber_image(Img& img, const typename Img::point_t& new_dims) { - img.recreate(new_dims); -} - -template -void resize_clobber_image(Img& img, const typename Img::x_coord_t& width, const typename Img::y_coord_t& height) { - img.recreate(width,height); -} - -template typename T::x_coord_t get_width(const T& a) { return a.width(); } -template typename T::y_coord_t get_height(const T& a) { return a.height(); } -template typename T::point_t get_dimensions(const T& a) { return a.dimensions(); } -template std::size_t get_num_channels(const T& a) { return a.num_channels(); } - -#define GIL boost::gil -#define ADOBE_GIL_NAMESPACE_BEGIN namespace boost { namespace gil { -#define ADOBE_GIL_NAMESPACE_END } } - -#define ByteAdvancableIteratorConcept MemoryBasedIteratorConcept -#define byte_advance memunit_advance -#define byte_advanced memunit_advanced -#define byte_step memunit_step -#define byte_distance memunit_distance - -#define byte_addressable_step_iterator memory_based_step_iterator -#define byte_addressable_2d_locator memory_based_2d_locator - -// These are members of memory-based locators -//#define row_bytes row_size // commented out because row_bytes is commonly used -#define pix_bytestep pixel_size - -#endif From bb080b5445ce35d6f2f34693aefc09338f814b43 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Thu, 7 Apr 2022 13:49:02 +0200 Subject: [PATCH 083/193] ci: Replace Ubuntu 16.04 with Ubuntu 18.04 in some GitHub Actions jobs (#640) * Replace Ubuntu 16.04 with Ubuntu 18.04 in GitHub Actions Ubuntu 16.04 is no longer available for GitHub Actions, because support for it has ended. Some older Clang versions seem to be unavailable in the APT repostiry, so those are not changed to 18.04 by intention. * Replace Ubuntu 16.04 with Ubuntu 18.04 in Azure Pipelines --- .azure-pipelines.yml | 8 ++++---- .github/workflows/ci.yml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index bf35a61860..a905ccbe1b 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -15,9 +15,9 @@ trigger: - ml/* jobs: - - job: 'ubuntu1604_gcc6_cxx14_cmake' + - job: 'ubuntu1804_gcc6_cxx14_cmake' pool: - vmImage: 'ubuntu-16.04' + vmImage: 'ubuntu-18.04' steps: - template: .ci/azure-pipelines/steps-install-gcc.yml parameters: @@ -34,9 +34,9 @@ jobs: parameters: cxxver: '14' - - job: 'ubuntu1604_gcc8_cxx14_cmake' + - job: 'ubuntu1804_gcc8_cxx14_cmake' pool: - vmImage: 'ubuntu-16.04' + vmImage: 'ubuntu-18.04' steps: - template: .ci/azure-pipelines/steps-install-gcc.yml parameters: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e6c356e4a..cf385d698f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: include: - toolset: gcc-6 cxxstd: "11,14,1z" - os: ubuntu-16.04 + os: ubuntu-18.04 install: g++-6 - toolset: gcc-7 cxxstd: "11,14,17" @@ -62,17 +62,17 @@ jobs: - toolset: clang compiler: clang++-3.9 cxxstd: "11,14" - os: ubuntu-16.04 + os: ubuntu-18.04 install: clang-3.9 - toolset: clang compiler: clang++-4.0 cxxstd: "11,14" - os: ubuntu-16.04 + os: ubuntu-18.04 install: clang-4.0 - toolset: clang compiler: clang++-5.0 cxxstd: "11,14,1z" - os: ubuntu-16.04 + os: ubuntu-18.04 install: clang-5.0 - toolset: clang compiler: clang++-6.0 From a38f890feec307d7251427db27aa442a111b6101 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Thu, 7 Apr 2022 20:01:02 +0200 Subject: [PATCH 084/193] ci: Fix Clang 9 build on GitHub Actions (#641) The name of the clang-9 package was missing, so it was not installed. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf385d698f..6f987ffce1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,6 +93,7 @@ jobs: compiler: clang++-9 cxxstd: "11,14,17,2a" os: ubuntu-20.04 + install: clang-9 - toolset: clang compiler: clang++-10 cxxstd: "11,14,17,2a" From c1c87ee0301c82a0acc328c1e69c2d79280db75b Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Thu, 7 Apr 2022 20:03:40 +0200 Subject: [PATCH 085/193] ci: Switch Azure's macOS build to supported OS version (#643) The image macOS-10.14 is not available anymore, but macOS-10.15 is still available. --- .azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index a905ccbe1b..092453e335 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -88,9 +88,9 @@ jobs: cxxver: '17' use_conan: 'ON' - - job: 'macos1014_xcode11_cmake' + - job: 'macos1015_xcode11_cmake' pool: - vmImage: 'macOS-10.14' + vmImage: 'macOS-10.15' steps: - script: which clang++ && clang++ --version displayName: 'Check clang' From cf1e6b11ba73d50e60fd26b0d84ec95031ab3672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 9 Apr 2022 11:49:50 +0200 Subject: [PATCH 086/193] build: Define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING for MSVC --- test/Jamfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/Jamfile b/test/Jamfile index 341ac443dc..9583ac6ff0 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -16,12 +16,20 @@ import regex ; import sequence ; import testing ; +# The header providing std::experimental::filesystem +# is deprecated by Microsoft and will be REMOVED. It is superseded by the C++17 +# header providing std::filesystem. +# You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING +# to acknowledge that you have received this warning. +local msvc-cxxs-with-experimental-fs = 11 14 ; + project : requirements . # TODO: Enable concepts check for all, not just test/core #BOOST_GIL_USE_CONCEPT_CHECK=1 + msvc,$(msvc-cxxs-with-experimental-fs):_SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING=1 [ requires cxx11_constexpr cxx11_defaulted_functions From 09c3464adf86818903442df9427a268526d43ce5 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Sat, 9 Apr 2022 13:26:41 +0200 Subject: [PATCH 087/193] ci: Switch GitHub Action's MSVC build to supported OS version (#644) Windows 2016 has been retired in March 2022, so it's not available anymore. For further information on that see . However, we can test on the newer Windows 2022 image instead. This also brings Visual Studio 2022. --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f987ffce1..b39adf5643 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,10 +147,6 @@ jobs: fail-fast: false matrix: include: - - toolset: msvc-14.1 - cxxstd: "14,17,latest" - addrmd: 32,64 - os: windows-2016 - toolset: msvc-14.2 cxxstd: "14,17,latest" addrmd: 32,64 @@ -159,6 +155,10 @@ jobs: cxxstd: "11,14,17,2a" addrmd: 64 os: windows-2019 + - toolset: msvc-14.3 + cxxstd: "14,17,latest" + addrmd: 32,64 + os: windows-2022 runs-on: ${{matrix.os}} From 8d83cdca24b42e0c844e0ff503931abe5a9fe590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 12 Apr 2022 17:25:39 +0200 Subject: [PATCH 088/193] ci: Remove /std:c++latest from MSVC build variants This should work around the Internal Compiler Error due to std::vector>, see https://github.com/boostorg/gil/issues/645 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b39adf5643..95690c761c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,7 +148,7 @@ jobs: matrix: include: - toolset: msvc-14.2 - cxxstd: "14,17,latest" + cxxstd: "14,17" addrmd: 32,64 os: windows-2019 - toolset: gcc @@ -156,7 +156,7 @@ jobs: addrmd: 64 os: windows-2019 - toolset: msvc-14.3 - cxxstd: "14,17,latest" + cxxstd: "14,17" addrmd: 32,64 os: windows-2022 From 22be33bb6198631266df214cf47dc85cae923a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 12 Apr 2022 19:24:49 +0200 Subject: [PATCH 089/193] ci: Remove cxxstd=2a from GitHub Actions ci.yml workflow This is workaround for clang issue with cxxstd=2a: error: no member named 'is_constant_evaluated' in namespace 'std' See https://github.com/boostorg/gil/pull/621#issuecomment-1096942965 --- .github/workflows/ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95690c761c..9aeb73898b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,14 +27,14 @@ jobs: os: ubuntu-18.04 - toolset: gcc compiler: g++-8 - cxxstd: "11,14,17,2a" + cxxstd: "11,14,17" os: ubuntu-18.04 install: g++-8 - toolset: gcc-9 - cxxstd: "11,14,17,2a" + cxxstd: "11,14,17" os: ubuntu-18.04 - toolset: gcc-10 - cxxstd: "11,14,17,2a" + cxxstd: "11,14,17" os: ubuntu-18.04 - toolset: clang compiler: clang++-3.5 @@ -86,20 +86,20 @@ jobs: install: clang-7 - toolset: clang compiler: clang++-8 - cxxstd: "11,14,17,2a" + cxxstd: "11,14,17" os: ubuntu-20.04 install: clang-8 - toolset: clang compiler: clang++-9 - cxxstd: "11,14,17,2a" + cxxstd: "11,14,17" os: ubuntu-20.04 install: clang-9 - toolset: clang compiler: clang++-10 - cxxstd: "11,14,17,2a" + cxxstd: "11,14,17" os: ubuntu-20.04 - toolset: clang - cxxstd: "11,14,17,2a" + cxxstd: "11,14,17" os: macos-10.15 runs-on: ${{matrix.os}} @@ -152,7 +152,7 @@ jobs: addrmd: 32,64 os: windows-2019 - toolset: gcc - cxxstd: "11,14,17,2a" + cxxstd: "11,14,17" addrmd: 64 os: windows-2019 - toolset: msvc-14.3 From 41167f6000c15d1aaf8e4d1a6f3f37d3689ff5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 12 Apr 2022 20:45:47 +0200 Subject: [PATCH 090/193] ci: Add Boost.Align to transitive dependencies on AppVeyor --- .ci/get-boost.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/get-boost.sh b/.ci/get-boost.sh index 985731d7a9..77604b5c30 100755 --- a/.ci/get-boost.sh +++ b/.ci/get-boost.sh @@ -49,6 +49,7 @@ git submodule --quiet update --init $GIT_SUBMODULE_OPTS \ libs/variant2 # Transitive (of GIL tests too) git submodule --quiet update --init $GIT_SUBMODULE_OPTS \ + libs/align \ libs/atomic \ libs/bind \ libs/chrono \ From 016ae78778e24a90cf2da37fa4e81995527d38d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 12 Apr 2022 20:56:28 +0200 Subject: [PATCH 091/193] ci: Stop building on CircleCI Closes #646 --- .circleci/config.yml | 654 ------------------------------------------- 1 file changed, 654 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index b56aaf123e..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,654 +0,0 @@ -############################################################################## -# CircleCI 2.0 for Boost.GIL -# -# Copyright (c) 2018 Mateusz Loskot -# -# Use, modification and distribution is 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) -############################################################################## -version: 2.0 - -############################################################################## -# Docker images -############################################################################## -docker_gcc48: &docker_gcc48 - docker: - - image: gcc:4.8 -docker_gcc49: &docker_gcc49 - docker: - - image: gcc:4.9 - -docker_gcc51: &docker_gcc51 - docker: - - image: gcc:5.1 -docker_gcc52: &docker_gcc52 - docker: - - image: gcc:5.2 -docker_gcc53: &docker_gcc53 - docker: - - image: gcc:5.3 -docker_gcc54: &docker_gcc54 - docker: - - image: gcc:5.4 -docker_gcc55: &docker_gcc55 - docker: - - image: gcc:5.5 - -docker_gcc61: &docker_gcc61 - docker: - - image: gcc:6.1 -docker_gcc62: &docker_gcc62 - docker: - - image: gcc:6.2 -docker_gcc63: &docker_gcc63 - docker: - - image: gcc:6.3 -docker_gcc64: &docker_gcc64 - docker: - - image: gcc:6.4 - -docker_gcc71: &docker_gcc71 - docker: - - image: gcc:7.3 -docker_gcc72: &docker_gcc72 - docker: - - image: gcc:7.2 -docker_gcc73: &docker_gcc73 - docker: - - image: gcc:7.3 - -docker_gcc82: &docker_gcc82 - docker: - - image: gcc:8.2 -docker_gcc83: &docker_gcc83 - docker: - - image: gcc:8.3 - -docker_clang39: &docker_clang39 - docker: - - image: rferraro/cxx-clang:3.9 - -docker_clang40: &docker_clang40 - docker: - - image: rferraro/cxx-clang:4.0 - -docker_clang50: &docker_clang50 - docker: - - image: rferraro/cxx-clang:5.0 - -############################################################################## -# Build variants -############################################################################## -env_gcc_cpp11_dbg: &env_gcc_cpp11_dbg - environment: - - TOOLSET: gcc - - VARIANT: "variant=debug" - - OPTIMIZ: "" - - CXXSTD: "11" - - LNKFLAG: "" - - B2_VERBOSE: "0" - -env_gcc_cpp11_opt_speed: &env_gcc_cpp11_opt_speed - environment: - - TOOLSET: gcc - - VARIANT: "variant=release" - - OPTIMIZ: "optimization=speed" - - CXXSTD: "11" - - LNKFLAG: "" - - B2_VERBOSE: "0" - -env_clang_cpp11_dbg: &env_clang_cpp11_dbg - environment: - - TOOLSET: clang - - VARIANT: "variant=debug" - - OPTIMIZ: "" - - CXXSTD: "11" - - LNKFLAG: "" - - B2_VERBOSE: "0" - -env_clang_cpp11_opt_speed: &env_clang_cpp11_opt_speed - environment: - - TOOLSET: clang - - VARIANT: "variant=release" - - OPTIMIZ: "optimization=speed" - - CXXSTD: "11" - - LNKFLAG: "" - - B2_VERBOSE: "0" - -############################################################################## -# Build configurations -############################################################################## -# GCC 4.8 is used to build `b2` driver -config_gcc48_cpp11_opt_speed: &config_gcc48_cpp11_opt_speed - <<: *docker_gcc48 - <<: *env_gcc_cpp11_opt_speed - -config_gcc49_cpp11_dbg: &config_gcc49_cpp11_dbg - <<: *docker_gcc49 - <<: *env_gcc_cpp11_dbg -config_gcc49_cpp11_opt_speed: &config_gcc49_cpp11_opt_speed - <<: *docker_gcc49 - <<: *env_gcc_cpp11_opt_speed - -config_gcc51_cpp11_dbg: &config_gcc51_cpp11_dbg - <<: *docker_gcc51 - <<: *env_gcc_cpp11_dbg -config_gcc51_cpp11_opt_speed: &config_gcc51_cpp11_opt_speed - <<: *docker_gcc51 - <<: *env_gcc_cpp11_opt_speed - -config_gcc52_cpp11_dbg: &config_gcc52_cpp11_dbg - <<: *docker_gcc52 - <<: *env_gcc_cpp11_dbg -config_gcc52_cpp11_opt_speed: &config_gcc52_cpp11_opt_speed - <<: *docker_gcc52 - <<: *env_gcc_cpp11_opt_speed - -config_gcc53_cpp11_dbg: &config_gcc53_cpp11_dbg - <<: *docker_gcc53 - <<: *env_gcc_cpp11_dbg -config_gcc53_cpp11_opt_speed: &config_gcc53_cpp11_opt_speed - <<: *docker_gcc53 - <<: *env_gcc_cpp11_opt_speed - -config_gcc54_cpp11_dbg: &config_gcc54_cpp11_dbg - <<: *docker_gcc54 - <<: *env_gcc_cpp11_dbg -config_gcc54_cpp11_opt_speed: &config_gcc54_cpp11_opt_speed - <<: *docker_gcc54 - <<: *env_gcc_cpp11_opt_speed - -config_gcc55_cpp11_dbg: &config_gcc55_cpp11_dbg - <<: *docker_gcc55 - <<: *env_gcc_cpp11_dbg -config_gcc55_cpp11_opt_speed: &config_gcc55_cpp11_opt_speed - <<: *docker_gcc55 - <<: *env_gcc_cpp11_opt_speed - -config_gcc61_cpp11_dbg: &config_gcc61_cpp11_dbg - <<: *docker_gcc61 - <<: *env_gcc_cpp11_dbg -config_gcc61_cpp11_opt_speed: &config_gcc61_cpp11_opt_speed - <<: *docker_gcc61 - <<: *env_gcc_cpp11_opt_speed - -config_gcc62_cpp11_dbg: &config_gcc62_cpp11_dbg - <<: *docker_gcc62 - <<: *env_gcc_cpp11_dbg -config_gcc62_cpp11_opt_speed: &config_gcc62_cpp11_opt_speed - <<: *docker_gcc62 - <<: *env_gcc_cpp11_opt_speed - -config_gcc63_cpp11_dbg: &config_gcc63_cpp11_dbg - <<: *docker_gcc63 - <<: *env_gcc_cpp11_dbg -config_gcc63_cpp11_opt_speed: &config_gcc63_cpp11_opt_speed - <<: *docker_gcc63 - <<: *env_gcc_cpp11_opt_speed - -config_gcc64_cpp11_dbg: &config_gcc64_cpp11_dbg - <<: *docker_gcc64 - <<: *env_gcc_cpp11_dbg -config_gcc64_cpp11_opt_speed: &config_gcc64_cpp11_opt_speed - <<: *docker_gcc64 - <<: *env_gcc_cpp11_opt_speed - -config_gcc71_cpp11_dbg: &config_gcc71_cpp11_dbg - <<: *docker_gcc71 - <<: *env_gcc_cpp11_dbg -config_gcc71_cpp11_opt_speed: &config_gcc71_cpp11_opt_speed - <<: *docker_gcc71 - <<: *env_gcc_cpp11_opt_speed - -config_gcc72_cpp11_dbg: &config_gcc72_cpp11_dbg - <<: *docker_gcc72 - <<: *env_gcc_cpp11_dbg -config_gcc72_cpp11_opt_speed: &config_gcc72_cpp11_opt_speed - <<: *docker_gcc72 - <<: *env_gcc_cpp11_opt_speed - -config_gcc73_cpp11_dbg: &config_gcc73_cpp11_dbg - <<: *docker_gcc73 - <<: *env_gcc_cpp11_dbg -config_gcc73_cpp11_opt_speed: &config_gcc73_cpp11_opt_speed - <<: *docker_gcc73 - <<: *env_gcc_cpp11_opt_speed - -config_gcc82_cpp11_dbg: &config_gcc82_cpp11_dbg - <<: *docker_gcc82 - <<: *env_gcc_cpp11_dbg -config_gcc82_cpp11_opt_speed: &config_gcc82_cpp11_opt_speed - <<: *docker_gcc82 - <<: *env_gcc_cpp11_opt_speed - -config_gcc83_cpp11_dbg: &config_gcc83_cpp11_dbg - <<: *docker_gcc83 - <<: *env_gcc_cpp11_dbg -config_gcc83_cpp11_opt_speed: &config_gcc83_cpp11_opt_speed - <<: *docker_gcc83 - <<: *env_gcc_cpp11_opt_speed - -config_clang39_cpp11_dbg: &config_clang39_cpp11_dbg - <<: *docker_clang39 - <<: *env_clang_cpp11_dbg -config_clang39_cpp11_opt_speed: &config_clang39_cpp11_opt_speed - <<: *docker_clang39 - <<: *env_clang_cpp11_opt_speed - -config_clang40_cpp11_dbg: &config_clang40_cpp11_dbg - <<: *docker_clang40 - <<: *env_clang_cpp11_dbg -config_clang40_cpp11_opt_speed: &config_clang40_cpp11_opt_speed - <<: *docker_clang40 - <<: *env_clang_cpp11_opt_speed - -config_clang50_cpp11_dbg: &config_clang50_cpp11_dbg - <<: *docker_clang50 - <<: *env_clang_cpp11_dbg -config_clang50_cpp11_opt_speed: &config_clang50_cpp11_opt_speed - <<: *docker_clang50 - <<: *env_clang_cpp11_opt_speed - -############################################################################## -# Workspace persistance -############################################################################## -attach_workspace: &attach_workspace - attach_workspace: - at: ~/project - -############################################################################## -# Build steps (in alphabetical order by name of steps group) -############################################################################## -run_compiler_version: &run_compiler_version - run: - name: Checking compiler version - command: | - echo TOOLSET=$TOOLSET - echo VARIANT=$VARIANT - echo OPTIMIZ=$OPTIMIZ - echo CXXSTD=$CXXSTD - echo LNKFLAG=$LNKFLAG - $TOOLSET --version - -steps_bootstrap: &steps_bootstrap - steps: - - *attach_workspace - - *run_compiler_version - - run: cd boost-root && ./bootstrap.sh -with-toolset=$TOOLSET || cat ./bootstrap.log - - run: cd boost-root && ./b2 -j2 headers > /dev/null - - persist_to_workspace: - root: ~/project - paths: boost-root - -steps_test_core: &steps_test_core - steps: - - *attach_workspace - - *run_compiler_version - - run: cd boost-root && ./b2 -j2 toolset=$TOOLSET $VARIANT $OPTIMIZ cxxstd="$CXXSTD" libs/gil/test/core - -steps_test_numeric: &steps_test_numeric - steps: - - *attach_workspace - - *run_compiler_version - - run: cd boost-root && ./b2 -j2 toolset=$TOOLSET $VARIANT $OPTIMIZ cxxstd="$CXXSTD" libs/gil/test/extension/numeric - -steps_test_toolbox: &steps_test_toolbox - steps: - - *attach_workspace - - *run_compiler_version - - run: cd boost-root && ./b2 -j2 toolset=$TOOLSET $VARIANT $OPTIMIZ cxxstd="$CXXSTD" libs/gil/test/extension/toolbox - -steps_test_io: &steps_test_io - steps: - - *attach_workspace - - *run_compiler_version - - run: cd boost-root && ./b2 -j2 toolset=$TOOLSET $VARIANT $OPTIMIZ cxxstd="$CXXSTD" libs/gil/test/extension/io//simple - -############################################################################## -# Build jobs -############################################################################## -jobs: - bootstrap_checkout: - working_directory: ~/project/gil - # Build `b2` using the ancient compiler to hopefully avoid run-time failures: - # ./b2: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.14' not found (required by ./b2) - <<: *config_gcc48_cpp11_opt_speed - steps: - - checkout - - run: - # CONTRIBUTING.md: Modular Boost Library Maintenance guide, for more realistic - # test environment, recommends to develop and test individual Boost library - # against other Boost libraries as defined by the Boost super-project master branch - name: Checkout Boost superproject - command: | - echo $PWD && ls -a $PWD - cd .. - echo $PWD && ls -a $PWD - ~/project/gil/.ci/get-boost.sh $CIRCLE_BRANCH ~/project/gil - cd boost-root - - persist_to_workspace: - root: ~/project - paths: boost-root - - bootstrap_gcc: - <<: *config_gcc48_cpp11_opt_speed - <<: *steps_bootstrap - - bootstrap_clang: - <<: *config_clang39_cpp11_opt_speed - <<: *steps_bootstrap - - ### GCC 4.x ################################################################ - gcc49_11_core: - <<: *config_gcc49_cpp11_dbg - <<: *steps_test_core - - gcc49_11_core_speed: - <<: *config_gcc49_cpp11_opt_speed - <<: *steps_test_core - - ### GCC 5.x ################################################################ - gcc51_11_core: - <<: *config_gcc51_cpp11_dbg - <<: *steps_test_core - - gcc51_11_core_speed: - <<: *config_gcc51_cpp11_opt_speed - <<: *steps_test_core - - gcc52_11_core: - <<: *config_gcc52_cpp11_dbg - <<: *steps_test_core - - gcc52_11_core_speed: - <<: *config_gcc52_cpp11_opt_speed - <<: *steps_test_core - - gcc53_11_core: - <<: *config_gcc53_cpp11_dbg - <<: *steps_test_core - - gcc53_11_core_speed: - <<: *config_gcc53_cpp11_opt_speed - <<: *steps_test_core - - gcc54_11_core: - <<: *config_gcc54_cpp11_dbg - <<: *steps_test_core - - gcc54_11_core_speed: - <<: *config_gcc54_cpp11_opt_speed - <<: *steps_test_core - - gcc55_11_core: - <<: *config_gcc55_cpp11_dbg - <<: *steps_test_core - - gcc55_11_core_speed: - <<: *config_gcc55_cpp11_opt_speed - <<: *steps_test_core - - ### GCC 6.x ################################################################ - gcc61_11_core: - <<: *config_gcc61_cpp11_dbg - <<: *steps_test_core - - gcc61_11_core_speed: - <<: *config_gcc61_cpp11_opt_speed - <<: *steps_test_core - - gcc62_11_core: - <<: *config_gcc62_cpp11_dbg - <<: *steps_test_core - - gcc62_11_core_speed: - <<: *config_gcc62_cpp11_opt_speed - <<: *steps_test_core - - gcc63_11_core: - <<: *config_gcc63_cpp11_dbg - <<: *steps_test_core - - gcc63_11_core_speed: - <<: *config_gcc63_cpp11_opt_speed - <<: *steps_test_core - - gcc64_11_core: - <<: *config_gcc64_cpp11_dbg - <<: *steps_test_core - - gcc64_11_core_speed: - <<: *config_gcc64_cpp11_opt_speed - <<: *steps_test_core - - ### GCC 7.x ################################################################ - gcc71_11_core: - <<: *config_gcc71_cpp11_dbg - <<: *steps_test_core - - gcc71_11_core_speed: - <<: *config_gcc71_cpp11_opt_speed - <<: *steps_test_core - - gcc72_11_core: - <<: *config_gcc72_cpp11_dbg - <<: *steps_test_core - - gcc72_11_core_speed: - <<: *config_gcc72_cpp11_opt_speed - <<: *steps_test_core - - gcc73_11_core: - <<: *config_gcc73_cpp11_dbg - <<: *steps_test_core - - gcc73_11_core_speed: - <<: *config_gcc73_cpp11_opt_speed - <<: *steps_test_core - - gcc82_11_core: - <<: *config_gcc82_cpp11_dbg - <<: *steps_test_core - - gcc82_11_core_speed: - <<: *config_gcc82_cpp11_opt_speed - <<: *steps_test_core - - gcc83_11_core: - <<: *config_gcc83_cpp11_dbg - <<: *steps_test_core - - gcc83_11_core_speed: - <<: *config_gcc83_cpp11_opt_speed - <<: *steps_test_core - - ### clang 3.9 ################################################################ - clang39_11_core: - <<: *config_clang39_cpp11_dbg - <<: *steps_test_core - - clang39_11_core_speed: - <<: *config_clang39_cpp11_opt_speed - <<: *steps_test_core - - ### clang 4.0 ################################################################ - clang40_11_core: - <<: *config_clang40_cpp11_dbg - <<: *steps_test_core - - clang40_11_core_speed: - <<: *config_clang40_cpp11_opt_speed - <<: *steps_test_core - - ### clang 5.0 ################################################################ - clang50_11_core: - <<: *config_clang50_cpp11_dbg - <<: *steps_test_core - - clang50_11_core_speed: - <<: *config_clang50_cpp11_opt_speed - <<: *steps_test_core - -############################################################################## -# Workflows -############################################################################## -workflows: - version: 2 - build-and-test: - jobs: - - bootstrap_checkout: - filters: { branches: { only: [ develop, master ] } } - - bootstrap_gcc: - requires: [ bootstrap_checkout ] - filters: { branches: { only: [ develop, master ] } } - - bootstrap_clang: - requires: [ bootstrap_checkout ] - filters: { branches: { only: [ develop, master ] } } - - ### GCC 4.x ############################################################ - - gcc49_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc49_11_core_speed: - requires: [ gcc49_11_core ] - filters: { branches: { only: [ develop, master ] } } - - ### GCC 5.x ############################################################ - - gcc51_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc51_11_core_speed: - requires: [ gcc51_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc52_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc52_11_core_speed: - requires: [ gcc52_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc53_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc53_11_core_speed: - requires: [ gcc53_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc54_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc54_11_core_speed: - requires: [ gcc54_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc55_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc55_11_core_speed: - requires: [ gcc55_11_core ] - filters: { branches: { only: [ develop, master ] } } - - ### GCC 6.x ############################################################ - - gcc61_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc61_11_core_speed: - requires: [ gcc61_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc62_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc62_11_core_speed: - requires: [ gcc62_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc63_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc63_11_core_speed: - requires: [ gcc63_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc64_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc64_11_core_speed: - requires: [ gcc64_11_core ] - filters: { branches: { only: [ develop, master ] } } - - ### GCC 7.x ############################################################ - - gcc71_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc71_11_core_speed: - requires: [ gcc71_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc72_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc72_11_core_speed: - requires: [ gcc72_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc73_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc73_11_core_speed: - requires: [ gcc73_11_core ] - filters: { branches: { only: [ develop, master ] } } - - ### GCC 8.x ############################################################ - - gcc82_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc82_11_core_speed: - requires: [ gcc82_11_core ] - filters: { branches: { only: [ develop, master ] } } - - - gcc83_11_core: - requires: [ bootstrap_gcc ] - filters: { branches: { only: [ develop, master ] } } - - gcc83_11_core_speed: - requires: [ gcc83_11_core ] - filters: { branches: { only: [ develop, master ] } } - - ### clang 3.9 ########################################################## - - clang39_11_core: - requires: [ bootstrap_clang ] - filters: { branches: { only: [ develop, master ] } } - - clang39_11_core_speed: - requires: [ clang39_11_core ] - filters: { branches: { only: [ develop, master ] } } - - ### clang 4.0 ########################################################## - - clang40_11_core: - requires: [ bootstrap_clang ] - filters: { branches: { only: [ develop, master ] } } - - clang40_11_core_speed: - requires: [ clang40_11_core ] - filters: { branches: { only: [ develop, master ] } } - - ### clang 5.0 ########################################################## - - clang50_11_core: - requires: [ bootstrap_clang ] - filters: { branches: { only: [ develop, master ] } } - - clang50_11_core_speed: - requires: [ clang50_11_core ] - filters: { branches: { only: [ develop, master ] } } - -############################################################################## -# Notifications -############################################################################## -# Circle CI 1.0 notifications seem to work for Circle CI 2.0 (not 2.1!) -# https://github.com/circleci/circleci-docs/issues/2411 -# https://discuss.circleci.com/t/circleci-2-0-notifications-webhooks/19075/9 -# -# TODO: Disabled until CircleCI supports single notification per whole workflow. -# Currently, it floods the webhook with a notification per job. -#notify: -# webhooks: -# - url: https://webhooks.gitter.im/e/9538066016dc0f9b6511 From c0cb52d1f88fbe0138e585a3779a353563a1041c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 12 Apr 2022 20:58:37 +0200 Subject: [PATCH 092/193] ci: Remove CircleCI traces from README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 50a0cbe349..2f4e11238a 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://bintray.com/bincrafters/public-conan/boost_gil%3Abincrafters) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://github.com/Microsoft/vcpkg/tree/master/ports/boost-gil) -Documentation | GitHub Actions | AppVeyor | Azure Pipelines | CircleCI | Regression | Codecov ---------------|----------------|------------|-----------------|-----------------|------------|---------- -[![develop](https://img.shields.io/badge/doc-develop-blue.svg)](https://boostorg.github.io/gil/develop/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/gil/actions?query=branch:develop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h/branch/develop?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/develop) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=develop)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=develop) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/develop.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/develop) | [![gil](https://img.shields.io/badge/gil-develop-blue.svg)](http://www.boost.org/development/tests/develop/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/develop) -[![master](https://img.shields.io/badge/doc-master-blue.svg)](https://boostorg.github.io/gil/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=master)](https://github.com/boostorg/gil/actions?query=branch:master) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/master) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=master)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=master) | [![CircleCI](https://circleci.com/gh/boostorg/gil/tree/master.svg?style=shield)](https://circleci.com/gh/boostorg/workflows/gil/tree/master) | [![gil](https://img.shields.io/badge/gil-master-blue.svg)](http://www.boost.org/development/tests/master/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/master) +Documentation | GitHub Actions | AppVeyor | Azure Pipelines | Regression | Codecov +--------------|----------------|----------|-----------------|------------|---------- +[![develop](https://img.shields.io/badge/doc-develop-blue.svg)](https://boostorg.github.io/gil/develop/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/gil/actions?query=branch:develop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h/branch/develop?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/develop) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=develop)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=develop) | [![gil](https://img.shields.io/badge/gil-develop-blue.svg)](http://www.boost.org/development/tests/develop/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/develop) +[![master](https://img.shields.io/badge/doc-master-blue.svg)](https://boostorg.github.io/gil/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=master)](https://github.com/boostorg/gil/actions?query=branch:master) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/master) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=master)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=master) | [![gil](https://img.shields.io/badge/gil-master-blue.svg)](http://www.boost.org/development/tests/master/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/master) # Boost.GIL From 39089c5f8f44f2a5a64a3704b831c0ee5a2c7f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Thu, 14 Apr 2022 08:48:49 +0200 Subject: [PATCH 093/193] docs: Add Discussions to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2f4e11238a..1445b66e8b 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ The official repository contains the following branches: There is number of communication channels to ask questions and discuss Boost.GIL issues: +- [GitHub Discussions](https://github.com/boostorg/gil/discussions/) - Mailing lists ([Boost discussion policy](https://www.boost.org/more/discussion_policy.html)) - [boost-gil](https://lists.boost.org/mailman/listinfo.cgi/boost-gil) (*recommended*) official Boost.GIL mailing list ([archive](https://lists.boost.org/boost-gil/)) - [boost-users](https://lists.boost.org/mailman/listinfo.cgi/boost-users) for all Boost users From caf92fa94ba4b5960b7431dd9c7e391400aa293d Mon Sep 17 00:00:00 2001 From: Paul92 Date: Thu, 21 Apr 2022 16:38:19 +0200 Subject: [PATCH 094/193] Renamed adaptive_he to adaptive_histogram_equalization (#638) --- example/Jamfile | 2 +- example/adaptive_he.md | 20 -------------------- example/adaptive_histogram_equalization.md | 20 ++++++++++++++++++++ example/histogram.cpp | 2 +- example/histogram_equalization.cpp | 2 +- example/histogram_matching.cpp | 2 +- 6 files changed, 24 insertions(+), 24 deletions(-) delete mode 100644 example/adaptive_he.md create mode 100644 example/adaptive_histogram_equalization.md diff --git a/example/Jamfile b/example/Jamfile index bf05e8323e..3b9de5a2a6 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -20,7 +20,7 @@ project # TODO: Add missing examples local sources = - adaptive_he.cpp + adaptive_histogram_equalization.cpp adaptive_threshold.cpp affine.cpp anisotropic_diffusion.cpp diff --git a/example/adaptive_he.md b/example/adaptive_he.md deleted file mode 100644 index 570d160957..0000000000 --- a/example/adaptive_he.md +++ /dev/null @@ -1,20 +0,0 @@ -# Adaptive Histogram Equalization - -Adaptive Histogram Equalization (AHE) capabilities in GIL are demonstrated by the program `adaptive_he`, compiled from the sources `example/adaptive_he.cpp`. - -## Synopsis -`adaptive_he` - -The program doesn't take any argument on the command line. - -`adaptive_he` expects to find an image called `test_adaptive.png` in the current directory, and produces the image `out-adaptive.png` in return. - -## Specific requirements - -### Build requirements -- A C++ compiler compliant with C++11 or above -- The PNG library installed and configured. - -### Execution requirements -- `adaptive_he` expects to find an image called `test_adaptive.png` in the current directory. - diff --git a/example/adaptive_histogram_equalization.md b/example/adaptive_histogram_equalization.md new file mode 100644 index 0000000000..9050af0b76 --- /dev/null +++ b/example/adaptive_histogram_equalization.md @@ -0,0 +1,20 @@ +# Adaptive Histogram Equalization + +Adaptive Histogram Equalization (AHE) capabilities in GIL are demonstrated by the program `adaptive_histogram_equalization`, compiled from the sources `example/adaptive_histogram_equalization.cpp`. + +## Synopsis +`adaptive_histogram_equalization` + +The program doesn't take any argument on the command line. + +`adaptive_histogram_equalization` expects to find an image called `test_adaptive.png` in the current directory, and produces the image `out-adaptive.png` in return. + +## Specific requirements + +### Build requirements +- A C++ compiler compliant with C++11 or above +- The PNG library installed and configured. + +### Execution requirements +- `adaptive_histogram_equalization` expects to find an image called `test_adaptive.png` in the current directory. + diff --git a/example/histogram.cpp b/example/histogram.cpp index 8adc40e9ca..6194808ef9 100644 --- a/example/histogram.cpp +++ b/example/histogram.cpp @@ -19,7 +19,7 @@ using namespace boost::gil; // See also: // histogram_equalization.cpp - Regular Histogram Equalization -// adaptive_he.cpp - Adaptive Histogram Equalization +// adaptive_histogram_equalization.cpp - Adaptive Histogram Equalization // histogram_matching.cpp - Reference-based histogram computation int main() diff --git a/example/histogram_equalization.cpp b/example/histogram_equalization.cpp index ae2ffd6329..d6d614c7f0 100644 --- a/example/histogram_equalization.cpp +++ b/example/histogram_equalization.cpp @@ -16,7 +16,7 @@ using namespace boost::gil; // See also: // histogram.cpp - General use of histograms in GIL -// adaptive_he.cpp - Adaptive Histogram Equalization +// adaptive_histogram_equalization.cpp - Adaptive Histogram Equalization // histogram_matching.cpp - Reference-based histogram computation int main() diff --git a/example/histogram_matching.cpp b/example/histogram_matching.cpp index 308b5fc1d6..e4b0e9ec9f 100644 --- a/example/histogram_matching.cpp +++ b/example/histogram_matching.cpp @@ -18,7 +18,7 @@ using namespace boost::gil; // See also: // histogram_equalization.cpp - Regular Histogram Equalization -// adaptive_he.cpp - Adaptive Histogram Equalization +// adaptive_histogram_equalization.cpp - Adaptive Histogram Equalization // histogram.cpp - General use of histograms in GIL std::vector> get_mask(gray8_view_t const& mask) From 4f83beb735463ed6ce68c67cb1607e82b23462cb Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 26 Apr 2022 20:52:56 +0200 Subject: [PATCH 095/193] style: Remove trailing space characters (#651) --- example/adaptive_threshold.cpp | 4 +-- example/harris.cpp | 4 +-- example/hessian.cpp | 10 +++--- example/histogram.cpp | 2 +- example/histogram_equalization.cpp | 14 ++++---- example/hvstack.hpp | 10 +++--- example/morphology.cpp | 2 +- example/rasterizer_ellipse.cpp | 4 +-- example/rasterizer_line.cpp | 2 +- example/threshold.cpp | 2 +- include/boost/gil/color_base.hpp | 2 +- .../gil/extension/dynamic_image/any_image.hpp | 4 +-- .../dynamic_image/any_image_view.hpp | 2 +- include/boost/gil/extension/histogram/std.hpp | 2 +- .../boost/gil/extension/numeric/affine.hpp | 34 +++++++++---------- include/boost/gil/histogram.hpp | 30 ++++++++-------- .../adaptive_histogram_equalization.hpp | 28 +++++++-------- .../histogram_equalization.hpp | 4 +-- .../image_processing/histogram_matching.hpp | 14 ++++---- include/boost/gil/promote_integral.hpp | 2 +- include/boost/gil/rasterization/ellipse.hpp | 20 +++++------ test/core/histogram/access.cpp | 2 +- test/core/histogram/constructor.cpp | 2 +- test/core/histogram/cumulative.cpp | 2 +- test/core/histogram/dimension.cpp | 2 +- test/core/histogram/fill.cpp | 34 +++++++++---------- test/core/histogram/helpers.cpp | 12 +++---- test/core/histogram/is_compatible.cpp | 4 +-- test/core/histogram/key.cpp | 13 ++++--- test/core/histogram/sub_histogram.cpp | 16 ++++----- test/core/histogram/utilities.cpp | 8 ++--- test/core/image/image.cpp | 2 +- test/core/image_processing/adaptive_he.cpp | 8 ++--- .../histogram_equalization.cpp | 6 ++-- .../image_processing/histogram_matching.cpp | 10 +++--- .../image_processing/threshold_binary.cpp | 6 ++-- test/core/rasterization/ellipse.cpp | 10 +++--- test/extension/dynamic_image/any_image.cpp | 2 +- test/extension/histogram/histogram.cpp | 4 +-- test/extension/io/targa/targa_old_test.cpp | 2 +- 40 files changed, 170 insertions(+), 171 deletions(-) diff --git a/example/adaptive_threshold.cpp b/example/adaptive_threshold.cpp index 3d0f650acd..1f8806f3f5 100644 --- a/example/adaptive_threshold.cpp +++ b/example/adaptive_threshold.cpp @@ -29,13 +29,13 @@ int main() boost::gil::threshold_adaptive(const_view(img), view(img_out), 11, threshold_adaptive_method::mean, threshold_direction::regular, 2); write_view("out-threshold-adaptive-mean.png", view(img_out), png_tag{}); - + boost::gil::threshold_adaptive(const_view(img), view(img_out), 11, threshold_adaptive_method::mean, threshold_direction::inverse, 2); write_view("out-threshold-adaptive-mean-inv.png", view(img_out), png_tag{}); boost::gil::threshold_adaptive(const_view(img), view(img_out), 7, threshold_adaptive_method::gaussian, threshold_direction::regular, 2); write_view("out-threshold-adaptive-gaussian.png", view(img_out), png_tag{}); - + boost::gil::threshold_adaptive(const_view(img), view(img_out), 11, threshold_adaptive_method::gaussian, threshold_direction::inverse, 2); write_view("out-threshold-adaptive-gaussian-inv.png", view(img_out), png_tag{}); diff --git a/example/harris.cpp b/example/harris.cpp index 0b9ebe6828..ea19c98ed0 100644 --- a/example/harris.cpp +++ b/example/harris.cpp @@ -161,11 +161,11 @@ int main(int argc, char* argv[]) gil::rgb8_image_t input_image; - gil::read_image(argv[1], input_image, gil::png_tag{}); + gil::read_image(argv[1], input_image, gil::png_tag{}); auto original_image = input_image; auto original_view = gil::view(original_image); auto input_view = gil::view(input_image); - auto grayscaled = to_grayscale(input_view); + auto grayscaled = to_grayscale(input_view); gil::gray8_image_t smoothed_image(grayscaled.dimensions()); auto smoothed = gil::view(smoothed_image); apply_gaussian_blur(gil::view(grayscaled), smoothed); diff --git a/example/hessian.cpp b/example/hessian.cpp index 50ac8b2e27..7f9db59484 100644 --- a/example/hessian.cpp +++ b/example/hessian.cpp @@ -1,9 +1,9 @@ -// +// // Copyright 2019 Olzhas Zhumabek -// -// Distributed under 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 +// +// Distributed under 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 diff --git a/example/histogram.cpp b/example/histogram.cpp index 6194808ef9..5dcdf2d1ee 100644 --- a/example/histogram.cpp +++ b/example/histogram.cpp @@ -45,7 +45,7 @@ int main() true // Use specified limits if this is true (default is false) ); - // Normalize the histogram + // Normalize the histogram h.normalize(); // Get a cumulative histogram from the histogram diff --git a/example/histogram_equalization.cpp b/example/histogram_equalization.cpp index d6d614c7f0..eec5c46a66 100644 --- a/example/histogram_equalization.cpp +++ b/example/histogram_equalization.cpp @@ -1,10 +1,10 @@ -// +// // Copyright 2020 Debabrata Mandal -// -// Distributed under 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 -// +// +// Distributed under 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 @@ -22,7 +22,7 @@ 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()); diff --git a/example/hvstack.hpp b/example/hvstack.hpp index b5bcc08bd8..9110b6f2de 100644 --- a/example/hvstack.hpp +++ b/example/hvstack.hpp @@ -1,9 +1,9 @@ -// +// // // Copyright 2021 Olzhas Zhumabek -// -// Distributed under 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 +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt // #include "boost/gil/image_view_factory.hpp" diff --git a/example/morphology.cpp b/example/morphology.cpp index 4e99d58709..33f244ae7d 100644 --- a/example/morphology.cpp +++ b/example/morphology.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) // User has to enter atleast one operation and they can enter maximum 8 // operations considering binary conversion to be an // operation.Output_image_template argument is the common component which - // will be added in names of all output images followed by a hyphen and + // will be added in names of all output images followed by a hyphen and // the operation name. // Example : // ./example_morphology morphology_original.png out black_hat top_hat diff --git a/example/rasterizer_ellipse.cpp b/example/rasterizer_ellipse.cpp index 1b31a5e5de..295fe0533c 100644 --- a/example/rasterizer_ellipse.cpp +++ b/example/rasterizer_ellipse.cpp @@ -24,12 +24,12 @@ namespace gil = boost::gil; int main() { - // Syntax for usage :- + // Syntax for usage :- // auto rasterizer = gil::midpoint_elliptical_rasterizer{}; // rasterizer(img_view, colour, center, semi-axes_length); // Where // img_view : gil view of the image on which ellipse is to be drawn. - // colour : Vector containing channel intensity values for img_view. Number of colours + // colour : Vector containing channel intensity values for img_view. Number of colours // provided must be equal to the number of channels present in img_view. // center : Array containing positive integer x co-ordinate and y co-ordinate of the center // respectively. diff --git a/example/rasterizer_line.cpp b/example/rasterizer_line.cpp index b1c3526f74..7b2aec35b1 100644 --- a/example/rasterizer_line.cpp +++ b/example/rasterizer_line.cpp @@ -17,7 +17,7 @@ namespace gil = boost::gil; // Demonstrates the use of a rasterizer to generate an image of a line // The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, // include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp -// The rasterizer used implements the Bresenham's line algorithm. +// The rasterizer used implements the Bresenham's line algorithm. // Multiple images are created, all of the same size, but with areas of different sizes passed to the rasterizer, resulting in different lines. // See also: // rasterizer_circle.cpp - Demonstrates the use of a rasterizer to generate an image of a circle diff --git a/example/threshold.cpp b/example/threshold.cpp index b71f071ba6..34703d97a3 100644 --- a/example/threshold.cpp +++ b/example/threshold.cpp @@ -21,7 +21,7 @@ using namespace boost::gil; // - threshold and regular: truncates the pixels whose values are greater than the threshold // - threshold and inverse: truncates the pixels whose values are less than the threshold // - zero and regular: zeroes the pixels whose values are less than the threshold -// - zero and inverse: zeroes the pixels whose values are greater than the threshold +// - zero and inverse: zeroes the pixels whose values are greater than the threshold // See also: // adaptive_threshold.cpp - Adaptive thresholding diff --git a/include/boost/gil/color_base.hpp b/include/boost/gil/color_base.hpp index b638aacbba..aa51f97faf 100644 --- a/include/boost/gil/color_base.hpp +++ b/include/boost/gil/color_base.hpp @@ -309,7 +309,7 @@ struct homogeneous_color_base homogeneous_color_base() : v0_{}, v1_{}, v2_{}, v3_{} {} explicit homogeneous_color_base(Element v) : v0_(v), v1_(v), v2_(v), v3_(v) {} - + homogeneous_color_base(Element v0, Element v1, Element v2, Element v3) : v0_(v0), v1_(v1), v2_(v2), v3_(v3) {} diff --git a/include/boost/gil/extension/dynamic_image/any_image.hpp b/include/boost/gil/extension/dynamic_image/any_image.hpp index 69b674b1f9..457ba4ace9 100644 --- a/include/boost/gil/extension/dynamic_image/any_image.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image.hpp @@ -90,13 +90,13 @@ class any_image : public variant2::variant { using parent_t = variant2::variant; -public: +public: using view_t = mp11::mp_rename, any_image_view>; using const_view_t = mp11::mp_rename, any_image_view>; using x_coord_t = std::ptrdiff_t; using y_coord_t = std::ptrdiff_t; using point_t = point; - + using parent_t::parent_t; any_image& operator=(any_image const& img) diff --git a/include/boost/gil/extension/dynamic_image/any_image_view.hpp b/include/boost/gil/extension/dynamic_image/any_image_view.hpp index 5d71b588b0..2a414e25ad 100644 --- a/include/boost/gil/extension/dynamic_image/any_image_view.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image_view.hpp @@ -76,7 +76,7 @@ class any_image_view : public variant2::variant { using parent_t = variant2::variant; -public: +public: using const_t = detail::views_get_const_t; using x_coord_t = std::ptrdiff_t; using y_coord_t = std::ptrdiff_t; diff --git a/include/boost/gil/extension/histogram/std.hpp b/include/boost/gil/extension/histogram/std.hpp index 58dcae80a9..a015078212 100644 --- a/include/boost/gil/extension/histogram/std.hpp +++ b/include/boost/gil/extension/histogram/std.hpp @@ -25,7 +25,7 @@ namespace boost { namespace gil { ////////////////////////////////////////////////////////// /// Histogram extension for STL container ////////////////////////////////////////////////////////// -/// \defgroup Histogram - STL Containers +/// \defgroup Histogram - STL Containers /// \brief Collection of functions to provide histogram support in GIL using Standard /// Template Library Containers /// The conversion from Boost.GIL images to compatible histograms are provided. The supported diff --git a/include/boost/gil/extension/numeric/affine.hpp b/include/boost/gil/extension/numeric/affine.hpp index 7b590e64a6..204a2877bb 100644 --- a/include/boost/gil/extension/numeric/affine.hpp +++ b/include/boost/gil/extension/numeric/affine.hpp @@ -108,7 +108,7 @@ boost::gil::matrix3x2 inverse(boost::gil::matrix3x2 m) return res; } -/// \fn gil::matrix3x2 center_rotate +/// \fn gil::matrix3x2 center_rotate /// \tparam T Data type for source image dimensions /// \tparam F Data type for angle through which image is to be rotated /// @param dims dimensions of source image @@ -116,39 +116,39 @@ boost::gil::matrix3x2 inverse(boost::gil::matrix3x2 m) /// @return A transformation matrix for rotating the source image about its center /// \brief rotates an image from its center point /// using consecutive affine transformations. -template +template boost::gil::matrix3x2 center_rotate(boost::gil::point dims,F rads) -{ +{ const F PI = F(3.141592653589793238); const F c_theta = std::abs(std::cos(rads)); const F s_theta = std::abs(std::sin(rads)); // Bound checks for angle rads - while(rads + PI < 0) - { - rads = rads + PI; + while(rads + PI < 0) + { + rads = rads + PI; } - while(rads > PI) - { - rads = rads - PI; + while(rads > PI) + { + rads = rads - PI; } // Basic Rotation Matrix boost::gil::matrix3x2 rotate = boost::gil::matrix3x2::get_rotate(rads); - + // Find distance for translating the image into view boost::gil::matrix3x2 translation(0,0,0,0,0,0); - if(rads > 0) - { - translation.b = s_theta; + if(rads > 0) + { + translation.b = s_theta; } - else - { + else + { translation.c = s_theta; } - if(std::abs(rads) > PI/2) + if(std::abs(rads) > PI/2) { translation.a = c_theta; translation.d = c_theta; @@ -165,7 +165,7 @@ boost::gil::matrix3x2 center_rotate(boost::gil::point dims,F rads) s_theta * dims.x / dims.y + c_theta ); - return scale * translate * rotate; + return scale * translate * rotate; } }} // namespace boost::gil diff --git a/include/boost/gil/histogram.hpp b/include/boost/gil/histogram.hpp index a74b590033..fbf3020255 100644 --- a/include/boost/gil/histogram.hpp +++ b/include/boost/gil/histogram.hpp @@ -60,12 +60,12 @@ inline typename std::enable_if::type } /// \ingroup Histogram-Helpers -/// \brief Functor provided for the hashing of tuples. -/// The following approach makes use hash_combine from -/// boost::container_hash. Although there is a direct hashing +/// \brief Functor provided for the hashing of tuples. +/// The following approach makes use hash_combine from +/// boost::container_hash. Although there is a direct hashing /// available for tuples, this approach will ease adopting in /// future to a std::hash_combine. In case std::hash extends -/// support to tuples this functor as well as the helper +/// support to tuples this functor as well as the helper /// implementation hash_tuple_impl can be removed. /// template @@ -115,7 +115,7 @@ bool tuple_compare(Tuple const& t1, Tuple const& t2, boost::mp11::index_sequence } /// \ingroup Histogram-Helpers -/// \brief Compares 2 tuples and outputs t1 <= t2 +/// \brief Compares 2 tuples and outputs t1 <= t2 /// Comparison is not in a lexicographic manner but on every element of the tuple hence /// (2, 2) > (1, 3) evaluates to false /// @@ -199,13 +199,13 @@ struct filler<1> /// \brief Default histogram class provided by boost::gil. /// /// The class inherits over the std::unordered_map provided by STL. A complete example/tutorial -/// of how to use the class resides in the docs. +/// of how to use the class resides in the docs. /// Simple calling syntax for a 3D dimensional histogram : /// \code /// histogram h; /// h(1, 1, 1) = 0; /// \endcode -/// This is just a starter to what all can be achieved with it, refer to the docs for the +/// This is just a starter to what all can be achieved with it, refer to the docs for the /// full demo. /// template @@ -237,7 +237,7 @@ class histogram : public std::unordered_map, double, detail::ha return base_t::operator[](key); } - /// \brief Checks if 2 histograms are equal. Ignores type, and checks if + /// \brief Checks if 2 histograms are equal. Ignores type, and checks if /// the keys (after type casting) match. template bool equals(OtherType const& otherhist) const @@ -258,7 +258,7 @@ class histogram : public std::unordered_map, double, detail::ha }); return check; } - + /// \brief Checks if the histogram class is compatible to be used with /// a GIL image type static constexpr bool is_pixel_compatible() @@ -502,7 +502,7 @@ class histogram : public std::unordered_map, double, detail::ha return sub_h; } - /// \brief Normalize this histogram class + /// \brief Normalize this histogram class void normalize() { double sum = 0.0; @@ -593,7 +593,7 @@ class histogram : public std::unordered_map, double, detail::ha /// \ingroup Histogram Algorithms /// \tparam SrcView Input image view /// \tparam Container Input histogram container -/// \brief Overload this function to provide support for boost::gil::histogram or +/// \brief Overload this function to provide support for boost::gil::histogram or /// any other external histogram /// /// Example : @@ -618,7 +618,7 @@ void fill_histogram(SrcView const&, Container&); /// @param lower Input Lower limit on the values in histogram (default numeric_limit::min() on axes) /// @param upper Input Upper limit on the values in histogram (default numeric_limit::max() on axes) /// @param setlimits Input Use specified limits if this is true (default is false) -/// \brief Overload version of fill_histogram +/// \brief Overload version of fill_histogram /// /// Takes a third argument to determine whether to clear container before filling. /// For eg, when there is a need to accumulate the histograms do @@ -643,11 +643,11 @@ void fill_histogram( { if (!accumulate) hist.clear(); - + detail::filler::dimension()> f; if (!sparsefill) f(hist, lower, upper, bin_width); - + hist.template fill(srcview, bin_width, applymask, mask, lower, upper, setlimits); } @@ -673,7 +673,7 @@ histogram cumulative_histogram(histogram const& hist) static_assert( boost::mp11::mp_all_of::value, "Cumulative histogram not possible of this type"); - + using histogram_t = histogram; using pair_t = std::pair; using value_t = typename histogram_t::value_type; diff --git a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp index 3ba0e6f55a..23bfb64547 100644 --- a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp +++ b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp @@ -27,9 +27,9 @@ namespace boost { namespace gil { /// \defgroup AHE AHE /// \brief Contains implementation and description of the algorithm used to compute /// adaptive histogram equalization of input images. Naming for the AHE functions -/// are done in the following way +/// are done in the following way /// __.._ahe -/// For example, for AHE done using local (non-overlapping) tiles/blocks and +/// For example, for AHE done using local (non-overlapping) tiles/blocks and /// final output interpolated among tiles , it is called /// non_overlapping_interpolated_clahe /// @@ -75,11 +75,11 @@ double actual_clip_limit(SrcHist const& src_hist, double cliplimit = 0.03) /// \fn void clip_and_redistribute /// \ingroup AHE-helpers -/// \brief Clips and redistributes excess pixels based on the actual clip limit value +/// \brief Clips and redistributes excess pixels based on the actual clip limit value /// obtained from the other helper function actual_clip_limit /// Reference - Graphic Gems 4, Pg. 474 /// (http://cas.xav.free.fr/Graphics%20Gems%204%20-%20Paul%20S.%20Heckbert.pdf) -/// +/// template void clip_and_redistribute(SrcHist const& src_hist, DstHist& dst_hist, double clip_limit = 0.03) { @@ -131,8 +131,8 @@ void clip_and_redistribute(SrcHist const& src_hist, DstHist& dst_hist, double cl /// @param src_mask Input Mask on input image to ignore specified pixels /// \brief Performs local histogram equalization on tiles of size (tile_width_x, tile_width_y) /// Then uses the clip limit to redistribute excess pixels above the limit uniformly to -/// other bins. The clip limit is specified as a fraction i.e. a bin's value is clipped -/// if bin_value >= clip_limit * (Total number of pixels in the tile) +/// other bins. The clip limit is specified as a fraction i.e. a bin's value is clipped +/// if bin_value >= clip_limit * (Total number of pixels in the tile) /// template void non_overlapping_interpolated_clahe( @@ -153,7 +153,7 @@ void non_overlapping_interpolated_clahe( typename color_space_type::type, typename color_space_type::type>::value, "Source and destination views must have same color space"); - + using source_channel_t = typename channel_type::type; using dst_channel_t = typename channel_type::type; using coord_t = typename SrcView::x_coord_t; @@ -192,7 +192,7 @@ void non_overlapping_interpolated_clahe( std::vector> prev_map( new_width / tile_width_x), next_map((new_width / tile_width_x)); - + coord_t prev = 0, next = 1; auto channel_view = nth_channel_view(view(padded_img), k); @@ -223,7 +223,7 @@ void non_overlapping_interpolated_clahe( fill_histogram( img_view, next_row[(j - sample_x1) / tile_width_x], bin_width, false, false); - + detail::clip_and_redistribute( next_row[(j - sample_x1) / tile_width_x], next_row[(j - sample_x1) / tile_width_x], clip_limit); @@ -244,7 +244,7 @@ void non_overlapping_interpolated_clahe( prev_col_mask = false; else if ((j - sample_x1) / tile_width_x + 1 == new_width / tile_width_x - 1) next_col_mask = false; - + // Bilinear interpolation point_t top_left( (j - sample_x1) / tile_width_x * tile_width_x + sample_x1, @@ -252,7 +252,7 @@ void non_overlapping_interpolated_clahe( point_t top_right(top_left.x + tile_width_x, top_left.y); point_t bottom_left(top_left.x, top_left.y + tile_width_y); point_t bottom_right(top_left.x + tile_width_x, top_left.y + tile_width_y); - + long double x_diff = top_right.x - top_left.x; long double y_diff = bottom_left.y - top_left.y; @@ -281,16 +281,16 @@ void non_overlapping_interpolated_clahe( (next_row_mask & next_col_mask) * x1 * next_map[(bottom_right.x - sample_x1) / tile_width_x][channel_view(j, i)]) * y1; - + if (mask && !src_mask[i - top_left_y][j - top_left_x]) { - dst_view(j - top_left_x, i - top_left_y) = + dst_view(j - top_left_x, i - top_left_y) = channel_convert( static_cast(channel_view(i, j))); } else { - dst_view(j - top_left_x, i - top_left_y) = + dst_view(j - top_left_x, i - top_left_y) = channel_convert(static_cast(numerator)); } } diff --git a/include/boost/gil/image_processing/histogram_equalization.hpp b/include/boost/gil/image_processing/histogram_equalization.hpp index d33c6f4663..033dd611b5 100644 --- a/include/boost/gil/image_processing/histogram_equalization.hpp +++ b/include/boost/gil/image_processing/histogram_equalization.hpp @@ -38,7 +38,7 @@ namespace boost { namespace gil { /// \ingroup HE /// \tparam SrcKeyType Key Type of input histogram /// @param src_hist INPUT Input source histogram -/// \brief Overload for histogram equalization algorithm, takes in a single source histogram +/// \brief Overload for histogram equalization algorithm, takes in a single source histogram /// and returns the color map used for histogram equalization. /// template @@ -111,7 +111,7 @@ void histogram_equalization( typename color_space_type::type, typename color_space_type::type>::value, "Source and destination views must have same color space"); - + // Defining channel type using source_channel_t = typename channel_type::type; using dst_channel_t = typename channel_type::type; diff --git a/include/boost/gil/image_processing/histogram_matching.hpp b/include/boost/gil/image_processing/histogram_matching.hpp index 4ab7f72abd..4ae5c10399 100644 --- a/include/boost/gil/image_processing/histogram_matching.hpp +++ b/include/boost/gil/image_processing/histogram_matching.hpp @@ -58,7 +58,7 @@ std::map /// @param src_hist INPUT source histogram /// @param ref_hist INPUT reference histogram /// @param dst_hist OUTPUT Output histogram -/// \brief Overload for histogram matching algorithm, takes in source histogram, reference +/// \brief Overload for histogram matching algorithm, takes in source histogram, reference /// histogram & destination histogram and returns the color map used for histogram /// matching as well as transforming the destination histogram. /// @@ -81,7 +81,7 @@ std::map histogram_matching( auto cumltv_srchist = cumulative_histogram(src_hist); auto cumltv_refhist = cumulative_histogram(ref_hist); std::map inverse_mapping; - + std::vector::key_type> src_keys, ref_keys; src_keys = src_hist.sorted_keys(); ref_keys = ref_hist.sorted_keys(); @@ -89,7 +89,7 @@ std::map histogram_matching( RefKeyType ref_max; if (start >= 0) ref_max = std::get<0>(ref_keys[start]); - + for (std::ptrdiff_t j = src_keys.size() - 1; j >= 0; --j) { double src_val = (cumltv_srchist[src_keys[j]] * ref_sum) / src_sum; @@ -101,7 +101,7 @@ std::map histogram_matching( std::abs(cumltv_refhist(std::min(ref_max, std::get<0>(ref_keys[start + 1]))) - src_val)) { - inverse_mapping[std::get<0>(src_keys[j])] = + inverse_mapping[std::get<0>(src_keys[j])] = std::min(ref_max, std::get<0>(ref_keys[start + 1])); } else @@ -126,8 +126,8 @@ std::map histogram_matching( /// @param mask INPUT Specify is mask is to be used /// @param src_mask INPUT Mask vector over input image /// @param ref_mask INPUT Mask vector over reference image -/// \brief Overload for histogram matching algorithm, takes in both source, reference & -/// destination image views and histogram matches the input image using the +/// \brief Overload for histogram matching algorithm, takes in both source, reference & +/// destination image views and histogram matches the input image using the /// reference image. /// template @@ -155,7 +155,7 @@ void histogram_matching( typename color_space_type::type, typename color_space_type::type>::value, "Source and destination view must have same color space"); - + // Defining channel type using source_channel_t = typename channel_type::type; using ref_channel_t = typename channel_type::type; diff --git a/include/boost/gil/promote_integral.hpp b/include/boost/gil/promote_integral.hpp index a12d341f70..914ee94a4c 100644 --- a/include/boost/gil/promote_integral.hpp +++ b/include/boost/gil/promote_integral.hpp @@ -2,7 +2,7 @@ // // Copyright (c) 2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle -// +// // Copyright (c) 2020, Debabrata Mandal // // Licensed under the Boost Software License version 1.0. diff --git a/include/boost/gil/rasterization/ellipse.hpp b/include/boost/gil/rasterization/ellipse.hpp index 43e51e8147..6ba1dafeb0 100644 --- a/include/boost/gil/rasterization/ellipse.hpp +++ b/include/boost/gil/rasterization/ellipse.hpp @@ -38,8 +38,8 @@ struct midpoint_elliptical_rasterizer // url: https://doi.ieeecomputersociety.org/10.1109/MCG.1984.275994 std::vector> trajectory_points; std::ptrdiff_t x = semi_axes[0], y = 0; - - // Variables declared on following lines are temporary variables used for improving + + // Variables declared on following lines are temporary variables used for improving // performance since they help in converting all multiplicative operations inside the while // loop into additive/subtractive operations. long long int const t1 = semi_axes[0] * semi_axes[0]; @@ -54,7 +54,7 @@ struct midpoint_elliptical_rasterizer // to be included in rasterizer trajectory. long long int d1, d2; d1 = t2 - t7 + t4 / 2, d2 = t1 / 2 - t8 + t5; - + while (d2 < 0) { trajectory_points.push_back({x, y}); @@ -65,7 +65,7 @@ struct midpoint_elliptical_rasterizer d1 += t9 + t2; d2 += t9; } - else + else { x -= 1; t8 -= t6; @@ -84,7 +84,7 @@ struct midpoint_elliptical_rasterizer t9 += t3; d2 += t5 + t9 - t8; } - else + else { d2 += t5 - t8; } @@ -99,7 +99,7 @@ struct midpoint_elliptical_rasterizer /// \param colour - Constant vector specifying colour intensity values for all channels present /// in 'view'. /// \param center - Constant array specifying co-ordinates of center of ellipse to be drawn. - /// \param trajectory_points - Constant vector specifying pixel co-ordinates of points lying + /// \param trajectory_points - Constant vector specifying pixel co-ordinates of points lying /// on rasterizer trajectory. /// \tparam View - Type of input image view. template @@ -107,7 +107,7 @@ struct midpoint_elliptical_rasterizer std::array const center, std::vector> const trajectory_points) { - for (int i = 0, colour_index = 0; i < static_cast(view.num_channels()); + for (int i = 0, colour_index = 0; i < static_cast(view.num_channels()); ++i, ++colour_index) { for (std::array point : trajectory_points) @@ -173,17 +173,17 @@ struct midpoint_elliptical_rasterizer "number of colours provided."); } if (center[0] + semi_axes[0] >= view.width() || center[1] + semi_axes[1] >= view.height() - || static_cast(center[0] - semi_axes[0]) < 0 + || static_cast(center[0] - semi_axes[0]) < 0 || static_cast(center[0] - semi_axes[0]) >= view.width() || static_cast(center[1] - semi_axes[1]) < 0 || static_cast(center[1] - semi_axes[1]) >= view.height()) { std::cout << "Image can't contain whole curve.\n" "However, it will contain those parts of curve which can fit inside it.\n" - "Note : Image width = " << view.width() << " and Image height = " << + "Note : Image width = " << view.width() << " and Image height = " << view.height() << "\n"; } - std::vector> trajectory_points = + std::vector> trajectory_points = obtain_trajectory(semi_axes); draw_curve(view, colour, center, trajectory_points); } diff --git a/test/core/histogram/access.cpp b/test/core/histogram/access.cpp index 0c1847e042..4420319ee9 100644 --- a/test/core/histogram/access.cpp +++ b/test/core/histogram/access.cpp @@ -14,7 +14,7 @@ namespace gil = boost::gil; -void check_indexing_operator() +void check_indexing_operator() { gil::histogram h1; h1(1) = 3; diff --git a/test/core/histogram/constructor.cpp b/test/core/histogram/constructor.cpp index 92817b8f9a..f1d1ca7e71 100644 --- a/test/core/histogram/constructor.cpp +++ b/test/core/histogram/constructor.cpp @@ -13,7 +13,7 @@ namespace gil = boost::gil; namespace mp11 = boost::mp11; -void check_histogram_constructors() +void check_histogram_constructors() { gil::histogram h1; gil::histogram h2 = h1; diff --git a/test/core/histogram/cumulative.cpp b/test/core/histogram/cumulative.cpp index c06f710890..34a8e1e028 100644 --- a/test/core/histogram/cumulative.cpp +++ b/test/core/histogram/cumulative.cpp @@ -25,7 +25,7 @@ void check_cumulative() { if(h2(i) != i+1) check1 = false; - } + } BOOST_TEST(check1); gil::histogram h3; diff --git a/test/core/histogram/dimension.cpp b/test/core/histogram/dimension.cpp index 912f15d27f..74dd3374f6 100644 --- a/test/core/histogram/dimension.cpp +++ b/test/core/histogram/dimension.cpp @@ -11,7 +11,7 @@ namespace gil = boost::gil; namespace mp11 = boost::mp11; -void check_histogram_constructors() +void check_histogram_constructors() { gil::histogram h1; gil::histogram h2 = h1; diff --git a/test/core/histogram/fill.cpp b/test/core/histogram/fill.cpp index ac43dc1f5a..e43a2faec4 100644 --- a/test/core/histogram/fill.cpp +++ b/test/core/histogram/fill.cpp @@ -23,15 +23,15 @@ gil::gray8_view_t v1 = view(img1); gil::rgb8_image_t img2(4, 4, gil::rgb8_pixel_t(1)); gil::rgb8_view_t v2 = view(img2); -std::uint8_t sparse_matrix[] = +std::uint8_t sparse_matrix[] = { - 1, 1, 1, 1, - 3, 3, 3, 3, + 1, 1, 1, 1, + 3, 3, 3, 3, 5, 5, 5, 5, 7, 7, 7, 7 }; -std::uint8_t big_matrix[] = +std::uint8_t big_matrix[] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 1, 2, 1, 2, 1, 2, @@ -43,7 +43,7 @@ std::uint8_t big_matrix[] = 7, 8, 7, 8, 7, 8, 7, 8 }; -std::uint8_t big_rgb_matrix[] = +std::uint8_t big_rgb_matrix[] = { 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, 7, 8, 9, 8, 9, 10, 1, 2, 3, 2, 3, 4, 1, 2, 3, 2, 3, 4, 1, 2, 3, 2, 3, 4, 1, 2, 3, 2, 3, 4, @@ -69,7 +69,7 @@ gil::gray8c_view_t big_gray_view = gil::interleaved_view(8, 8, reinterpret_cast< gil::rgb8c_view_t big_rgb_view = gil::interleaved_view(8, 8, reinterpret_cast(big_rgb_matrix), 24); -void check_histogram_fill_test1() +void check_histogram_fill_test1() { gil::histogram h1; @@ -86,7 +86,7 @@ void check_histogram_fill_test1() BOOST_TEST(check_gray_fill); } -void check_histogram_fill_test2() +void check_histogram_fill_test2() { gil::histogram h3; h3.fill(big_rgb_view); @@ -102,7 +102,7 @@ void check_histogram_fill_test2() BOOST_TEST(check_rgb_fill); } -void check_histogram_fill_test3() +void check_histogram_fill_test3() { gil::histogram h2; h2.fill<1>(big_rgb_view); @@ -117,7 +117,7 @@ void check_histogram_fill_test3() BOOST_TEST(check_gray_fill2); } -void check_histogram_fill_test4() +void check_histogram_fill_test4() { gil::histogram h1; // Check with limits @@ -140,7 +140,7 @@ void check_histogram_fill_test4() BOOST_TEST(check_gray_fill); } -void check_histogram_fill_test5() +void check_histogram_fill_test5() { gil::histogram h3; std::tuple lower1{2,2,2}, higher1{6,6,6}; @@ -163,7 +163,7 @@ void check_histogram_fill_test5() BOOST_TEST(check_rgb_fill); } -void check_histogram_fill_test6() +void check_histogram_fill_test6() { gil::histogram h2; h2.clear(); @@ -185,13 +185,13 @@ void check_histogram_fill_test6() BOOST_TEST(check_gray_fill2); } -void check_histogram_fill_test7() +void check_histogram_fill_test7() { //Check masking gil::histogram h4; std::tuple low{1}, high{8}; gil::fill_histogram(sparse_gray_view, h4, 1, false, false, true, mask, low, high, true); - + bool check_1d = true; for (std::size_t i = 1; i <= 8; ++i) { @@ -203,12 +203,12 @@ void check_histogram_fill_test7() BOOST_TEST(check_1d); } -void check_histogram_fill_algorithm() +void check_histogram_fill_algorithm() { gil::histogram h1; gil::fill_histogram<1>(big_rgb_view, h1); - + bool check_1d = true; for (std::size_t i = 1; i <= 8; ++i) { @@ -222,7 +222,7 @@ void check_histogram_fill_algorithm() gil::histogram h2; gil::fill_histogram<2, 1>(big_rgb_view, h2); - + bool check_2d = true; for (std::size_t i = 1; i <= 8; ++i) { @@ -237,7 +237,7 @@ void check_histogram_fill_algorithm() std::tuple low(1), high(8); gil::fill_histogram(sparse_gray_view, h3, 1, false, false, false, {{}}, low, high, true); - + check_1d = true; for (std::size_t i = 1; i <= 8; ++i) { diff --git a/test/core/histogram/helpers.cpp b/test/core/histogram/helpers.cpp index 38fafd9f2a..ee4797c8f6 100644 --- a/test/core/histogram/helpers.cpp +++ b/test/core/histogram/helpers.cpp @@ -18,7 +18,7 @@ namespace gil = boost::gil; namespace mp11 = boost::mp11; -void check_helper_fn_pixel_to_tuple() +void check_helper_fn_pixel_to_tuple() { gil::gray8_pixel_t g1(2); auto g2 = gil::detail::pixel_to_tuple(g1, mp11::make_index_sequence<1>{}); @@ -39,7 +39,7 @@ void check_helper_fn_pixel_to_tuple() BOOST_TEST(r1[0] == std::get<2>(r3) && r1[1] == std::get<0>(r3) && r1[2] == std::get<1>(r3)); } -void check_helper_fn_tuple_to_tuple() +void check_helper_fn_tuple_to_tuple() { std::tuple t1(1); auto t2 = gil::detail::tuple_to_tuple(t1, mp11::make_index_sequence<1>{}); @@ -54,13 +54,13 @@ void check_helper_fn_tuple_to_tuple() bool const same_rgb_type = std::is_same, decltype(r2)>::value; BOOST_TEST(same_rgb_type); - BOOST_TEST( std::get<0>(r1) == std::get<0>(r2) && - std::get<1>(r1) == std::get<1>(r2) && + BOOST_TEST( std::get<0>(r1) == std::get<0>(r2) && + std::get<1>(r1) == std::get<1>(r2) && std::get<2>(r1) == std::get<2>(r2)); auto r3 = gil::detail::tuple_to_tuple(r1, mp11::index_sequence<1, 2, 0>{}); - BOOST_TEST( std::get<0>(r1) == std::get<2>(r3) && - std::get<1>(r1) == std::get<0>(r3) && + BOOST_TEST( std::get<0>(r1) == std::get<2>(r3) && + std::get<1>(r1) == std::get<0>(r3) && std::get<2>(r1) == std::get<1>(r3)); } diff --git a/test/core/histogram/is_compatible.cpp b/test/core/histogram/is_compatible.cpp index bb150943a9..30259a98d3 100644 --- a/test/core/histogram/is_compatible.cpp +++ b/test/core/histogram/is_compatible.cpp @@ -16,7 +16,7 @@ namespace gil = boost::gil; namespace mp11 = boost::mp11; -void check_is_pixel_compatible() +void check_is_pixel_compatible() { gil::histogram h1; gil::histogram h2; @@ -29,7 +29,7 @@ void check_is_pixel_compatible() BOOST_TEST(!h4.is_pixel_compatible()); } -void check_is_tuple_compatible() +void check_is_tuple_compatible() { gil::histogram h1; gil::histogram h2; diff --git a/test/core/histogram/key.cpp b/test/core/histogram/key.cpp index e4e2418aef..3029c46c8f 100644 --- a/test/core/histogram/key.cpp +++ b/test/core/histogram/key.cpp @@ -16,31 +16,31 @@ namespace gil = boost::gil; -void check_histogram_key_from_tuple() +void check_histogram_key_from_tuple() { gil::histogram h1; std::tuple t1(1, 2); auto t2 = h1.key_from_tuple(t1); const bool same_type = std::is_same, decltype(t2)>::value; - + BOOST_TEST(same_type); BOOST_TEST(std::get<0>(t2) == 1 && std::get<1>(t2) == 2); std::tuple t3(1, 2, 4, 2); auto t4 = h1.key_from_tuple<0, 2>(t3); const bool same_type1 = std::is_same, decltype(t4)>::value; - + BOOST_TEST(same_type1); BOOST_TEST(std::get<0>(t4) == 1 && std::get<1>(t4) == 4); } -void check_histogram_key_from_pixel() +void check_histogram_key_from_pixel() { gil::histogram h1; gil::gray8_pixel_t g1(1); auto t1 = h1.key_from_pixel(g1); const bool same_type = std::is_same, decltype(t1)>::value; - + BOOST_TEST(same_type); BOOST_TEST(std::get<0>(t1) == 1); @@ -48,7 +48,7 @@ void check_histogram_key_from_pixel() gil::rgb8_pixel_t r1(1, 0, 3); auto t2 = h2.key_from_pixel<0, 2>(r1); const bool same_type1 = std::is_same, decltype(t2)>::value; - + BOOST_TEST(same_type1); BOOST_TEST(std::get<0>(t2) == 1 && std::get<1>(t2) == 3); } @@ -60,4 +60,3 @@ int main() { return boost::report_errors(); } - \ No newline at end of file diff --git a/test/core/histogram/sub_histogram.cpp b/test/core/histogram/sub_histogram.cpp index af618b2334..15748611a6 100644 --- a/test/core/histogram/sub_histogram.cpp +++ b/test/core/histogram/sub_histogram.cpp @@ -14,7 +14,7 @@ namespace gil = boost::gil; -void check_sub_histogram_without_tuple() +void check_sub_histogram_without_tuple() { gil::histogram h; h(1, 1, "A", 1) = 1; @@ -23,13 +23,13 @@ void check_sub_histogram_without_tuple() h(2, 1, "D", 1) = 1; h(2, 3, "E", 4) = 1; auto h1 = h.sub_histogram<0,3>(); - BOOST_TEST(h1(1, 1) == 2); - BOOST_TEST(h1(2, 1) == 2); - BOOST_TEST(h1(2, 4) == 1); + BOOST_TEST(h1(1, 1) == 2); + BOOST_TEST(h1(2, 1) == 2); + BOOST_TEST(h1(2, 4) == 1); BOOST_TEST(h1(5, 5) == 0); } -void check_sub_histogram_with_tuple() +void check_sub_histogram_with_tuple() { gil::histogram h; h(1, 1, "A", 1) = 3; @@ -41,9 +41,9 @@ void check_sub_histogram_with_tuple() std::tuple t(1.0, 1000, "A", 1); // This means 1st dimension is useless for matching. auto h1 = h.sub_histogram<0,2,3>(t, t); - BOOST_TEST(h1(1, 1, "A", 1) == 3); - BOOST_TEST(h1(1, 2, "C", 1) == 0); - BOOST_TEST(h1(2, 1, "C", 1) == 0); + BOOST_TEST(h1(1, 1, "A", 1) == 3); + BOOST_TEST(h1(1, 2, "C", 1) == 0); + BOOST_TEST(h1(2, 1, "C", 1) == 0); BOOST_TEST(h1(2, 1, "A", 1) == 0); BOOST_TEST(h1(2, 1, "A", 1) == 0); BOOST_TEST(h1(1, 3, "A", 1) == 2); diff --git a/test/core/histogram/utilities.cpp b/test/core/histogram/utilities.cpp index 72b40e9477..e1c8642ca0 100644 --- a/test/core/histogram/utilities.cpp +++ b/test/core/histogram/utilities.cpp @@ -19,7 +19,7 @@ namespace gil = boost::gil; namespace mp11 = boost::mp11; -std::uint8_t big_matrix[] = +std::uint8_t big_matrix[] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 1, 2, 1, 2, 1, 2, @@ -52,7 +52,7 @@ void check_normalize() bool check = true; for (std::size_t i = 0; i < 64; i++) { - check = check & (std::abs(expected[i] - h1(i)) < epsilon); + check = check & (std::abs(expected[i] - h1(i)) < epsilon); } BOOST_TEST(check); @@ -74,7 +74,7 @@ void check_normalize() bool check2 = true; for (std::size_t i = 0; i < 64; i++) { - check2 = check2 & (std::abs(expected2[i/8][i%8] - h2(i/8,i%8)) < epsilon); + check2 = check2 & (std::abs(expected2[i/8][i%8] - h2(i/8,i%8)) < epsilon); } BOOST_TEST(check2); } @@ -212,4 +212,4 @@ int main() { check_sorted_keys(); return boost::report_errors(); -} +} diff --git a/test/core/image/image.cpp b/test/core/image/image.cpp index df61a3df92..1ab02f1fda 100644 --- a/test/core/image/image.cpp +++ b/test/core/image/image.cpp @@ -49,7 +49,7 @@ struct test_constructor_from_other_image pixel_t const rnd_pixel = fixture::pixel_generator::random(); { //constructor interleaved from planar - gil::image image1(dimensions, rnd_pixel); + gil::image image1(dimensions, rnd_pixel); image_t image2(image1); BOOST_TEST_EQ(image2.dimensions(), dimensions); auto v1 = gil::const_view(image1); diff --git a/test/core/image_processing/adaptive_he.cpp b/test/core/image_processing/adaptive_he.cpp index b625d0b6b5..61e65d7eab 100644 --- a/test/core/image_processing/adaptive_he.cpp +++ b/test/core/image_processing/adaptive_he.cpp @@ -20,10 +20,10 @@ namespace gil = boost::gil; double epsilon = 1.0; -std::uint8_t image_matrix[] = +std::uint8_t image_matrix[] = { - 1, 1, 1, 1, - 3, 3, 3, 3, + 1, 1, 1, 1, + 3, 3, 3, 3, 5, 5, 5, 5, 7, 7, 7, 7 }; @@ -72,7 +72,7 @@ void check_clip_and_redistribute() } } bool check = true; - double limit = 0.001; + double limit = 0.001; gil::detail::clip_and_redistribute(h, h2, limit); for(std::size_t i = 0; i < 100; i++) { diff --git a/test/core/image_processing/histogram_equalization.cpp b/test/core/image_processing/histogram_equalization.cpp index 8aab634a03..cf0e53dc79 100644 --- a/test/core/image_processing/histogram_equalization.cpp +++ b/test/core/image_processing/histogram_equalization.cpp @@ -18,7 +18,7 @@ #include const int a = 5; -const double epsilon = 0.005; // Decided by the value 1/255 i.e. an error of 1 px in 255 px +const double epsilon = 0.005; // Decided by the value 1/255 i.e. an error of 1 px in 255 px boost::gil::gray8_image_t original(a, a); boost::gil::gray8_image_t processed_1(a, a), processed_2(a, a), expected(a, a); std::vector > test1_random{ @@ -115,7 +115,7 @@ void vector_to_gray_image(boost::gil::gray8_image_t& img, { for(std::ptrdiff_t x=0; x const int a = 5; -const double epsilon = 0.000001; // Decided by the value 5/255 i.e. an error of 5 px in 255 px +const double epsilon = 0.000001; // Decided by the value 5/255 i.e. an error of 5 px in 255 px boost::gil::gray8_image_t original(a, a), reference(a, a); boost::gil::gray8_image_t processed(a, a), processed2(a, a); std::vector > test1_random{ @@ -67,7 +67,7 @@ void vector_to_gray_image(boost::gil::gray8_image_t& img, { for(std::ptrdiff_t x=0; x::max(); channel_t min_p = std::numeric_limits::min(); long int num_pixels = v1.width() * v1.height(); - + boost::gil::fill_histogram(v1, h1, 1, false, false); boost::gil::fill_histogram(v2, h2, 1, false, false); auto ch1 = boost::gil::cumulative_histogram(h1); @@ -110,7 +110,7 @@ void test_uniform_image() vector_to_gray_image(reference,test2_reference); histogram_matching(boost::gil::const_view(original),boost::gil::const_view(reference),boost::gil::view(processed)); BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(reference))); - + histogram_matching(boost::gil::const_view(processed),boost::gil::const_view(reference),boost::gil::view(processed2)); BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(processed2))); } @@ -121,7 +121,7 @@ void test_equal_image() vector_to_gray_image(reference,test3_reference); histogram_matching(boost::gil::const_view(original),boost::gil::const_view(reference),boost::gil::view(processed)); BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(reference))); - + histogram_matching(boost::gil::const_view(processed),boost::gil::const_view(reference),boost::gil::view(processed2)); BOOST_TEST(equal_histograms(boost::gil::view(processed), boost::gil::view(processed2))); } diff --git a/test/core/image_processing/threshold_binary.cpp b/test/core/image_processing/threshold_binary.cpp index 098cfc0628..fc39046454 100644 --- a/test/core/image_processing/threshold_binary.cpp +++ b/test/core/image_processing/threshold_binary.cpp @@ -87,7 +87,7 @@ 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 + //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, @@ -102,7 +102,7 @@ void binary_rgb_to_rgb() void binary_inverse_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 90, 0, 0 + //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)); @@ -127,7 +127,7 @@ int main() { fill_original_gray(); fill_original_rgb(); - + binary_gray_to_gray(); binary_inverse_gray_to_gray(); binary_rgb_to_rgb(); diff --git a/test/core/rasterization/ellipse.cpp b/test/core/rasterization/ellipse.cpp index 1b9e813136..58b5f43458 100644 --- a/test/core/rasterization/ellipse.cpp +++ b/test/core/rasterization/ellipse.cpp @@ -13,7 +13,7 @@ namespace gil = boost::gil; -// This function utilizes the fact that sum of distances of a point on an ellipse from its foci +// This function utilizes the fact that sum of distances of a point on an ellipse from its foci // is equal to the length of major axis of the ellipse. // Parameters b and a represent half of lengths of vertical and horizontal axis respectively. void test_rasterizer_follows_equation( @@ -30,21 +30,21 @@ void test_rasterizer_follows_equation( focus_x = 0; focus_y = b * std::sqrt(1 - a * a / (b * b)); } - + for (auto trajectory_point : trajectory_points) { // To suppress conversion warnings from compiler std::array point { static_cast(trajectory_point[0]), static_cast(trajectory_point[1])}; - double dist_sum = std::sqrt(std::pow(focus_x - point[0], 2) + - std::pow(focus_y - point[1], 2)) + std::sqrt(std::pow( - focus_x - point[0], 2) + + double dist_sum = std::sqrt(std::pow(focus_x - point[0], 2) + + std::pow(focus_y - point[1], 2)) + std::sqrt(std::pow( - focus_x - point[0], 2) + std::pow( - focus_y - point[1], 2)); if (a > b) { BOOST_TEST(std::abs(dist_sum - 2 * a) < 1); } - else + else { BOOST_TEST(std::abs(dist_sum - 2 * b) < 1); } diff --git a/test/extension/dynamic_image/any_image.cpp b/test/extension/dynamic_image/any_image.cpp index c8efc58256..68ead86bba 100644 --- a/test/extension/dynamic_image/any_image.cpp +++ b/test/extension/dynamic_image/any_image.cpp @@ -25,7 +25,7 @@ struct test_any_image_move_ctor fixture::dynamic_image i0(fixture::create_image(4, 4, 128)); BOOST_TEST_EQ(i0.dimensions().x, 4); BOOST_TEST_EQ(i0.dimensions().y, 4); - + fixture::dynamic_image i1 = fixture::create_image(4, 4, 128); BOOST_TEST_EQ(i1.dimensions().x, 4); BOOST_TEST_EQ(i1.dimensions().y, 4); diff --git a/test/extension/histogram/histogram.cpp b/test/extension/histogram/histogram.cpp index 6b4b23951e..5cfbdab72f 100644 --- a/test/extension/histogram/histogram.cpp +++ b/test/extension/histogram/histogram.cpp @@ -17,7 +17,7 @@ #include #include -// Basic tests to make sure compatible container types produce +// Basic tests to make sure compatible container types produce // expected output histogram. namespace gil = boost::gil; @@ -167,7 +167,7 @@ int main() check_fill_histogram_vector(); check_fill_histogram_array(); check_fill_histogram_map(); - + check_cumulative_histogram_vector(); check_cumulative_histogram_array(); check_cumulative_histogram_map(); diff --git a/test/extension/io/targa/targa_old_test.cpp b/test/extension/io/targa/targa_old_test.cpp index b1f4bcbcb5..d92f011dcd 100644 --- a/test/extension/io/targa/targa_old_test.cpp +++ b/test/extension/io/targa/targa_old_test.cpp @@ -71,7 +71,7 @@ void test_old_dynamic_image() gil::rgb8_image_t, gil::rgba8_image_t > image; - + gil::targa_read_image(targa_filename.c_str(), image); targa_write_view(targa_out + "old_dynamic_image_test.tga", gil::view(image)); From 9ed8fd2b4ba2033cfe2a5dc5de61b90fddea00d0 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 26 Apr 2022 21:07:32 +0200 Subject: [PATCH 096/193] ci: remove Windows Server 2016 from builds on Azure Pipelines (#652) The images with Windows Server 2016 / Visual Studio 2017 have been deprecated by Microsoft and are in the process of being removed completely, so that no further builds with that image will be possible. For reference, see * * Since it seems that Microsoft is basically using the same build environments on both Azure and GitHub Actions when it comes to Windows and Visual Studio, I decided to remove those jobs instead of updating them to a newer version of the image. The tests that would be possible with newer images on Azure are basically what we are already doing with GitHub Actions. --- .azure-pipelines.yml | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 092453e335..0a5e394f5d 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -53,41 +53,6 @@ jobs: parameters: cxxver: '14' - - job: 'win2016_vs2017_cxx14_cmake' - pool: - vmImage: 'vs2017-win2016' - steps: - - task: UsePythonVersion@0 - displayName: 'Setup Python' - inputs: - versionSpec: '3.6' - addToPath: true - architecture: 'x64' - - template: .ci/azure-pipelines/steps-check-cmake.yml - - template: .ci/azure-pipelines/steps-install-conan.yml - - template: .ci/azure-pipelines/steps-install-boost.yml - - template: .ci/azure-pipelines/steps-cmake-build-and-test.yml - parameters: - use_conan: 'ON' - - - job: 'win2016_vs2017_cxx17_cmake' - pool: - vmImage: 'vs2017-win2016' - steps: - - task: UsePythonVersion@0 - displayName: 'Setup Python' - inputs: - versionSpec: '3.6' - addToPath: true - architecture: 'x64' - - template: .ci/azure-pipelines/steps-check-cmake.yml - - template: .ci/azure-pipelines/steps-install-conan.yml - - template: .ci/azure-pipelines/steps-install-boost.yml - - template: .ci/azure-pipelines/steps-cmake-build-and-test.yml - parameters: - cxxver: '17' - use_conan: 'ON' - - job: 'macos1015_xcode11_cmake' pool: vmImage: 'macOS-10.15' From 791148660e1c4244f7668ad376572eb8b7677d15 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 26 Apr 2022 21:27:22 +0200 Subject: [PATCH 097/193] fix: is_equal_to_sixteen in PNG I/O was less-than test (#650) While is_equal_to_sixteen should (as the name suggests) check for equality to 16, it actually checked whether the bit depth was less than 16. --- .../gil/extension/io/png/detail/write.hpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/include/boost/gil/extension/io/png/detail/write.hpp b/include/boost/gil/extension/io/png/detail/write.hpp index eee5b9ed1c..02c5422e68 100644 --- a/include/boost/gil/extension/io/png/detail/write.hpp +++ b/include/boost/gil/extension/io/png/detail/write.hpp @@ -171,10 +171,24 @@ class writer< Device {}; template - struct is_equal_to_sixteen : mp11::mp_less + struct is_equal_to_sixteen : mp11::mp_and < - std::integral_constant, - std::integral_constant + mp11::mp_not + < + mp11::mp_less + < + std::integral_constant, + std::integral_constant + > + >, + mp11::mp_not + < + mp11::mp_less + < + std::integral_constant, + std::integral_constant + > + > > {}; From 9d526ed70673bc69192b86cb4411219fcf569721 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Wed, 27 Apr 2022 13:27:12 +0200 Subject: [PATCH 098/193] refactor: Renamed io/dynamic_io_new.hpp to io/detail/dynamic.hpp (#653) Closes #189 --- .../gil/extension/io/bmp/detail/read.hpp | 4 ++-- .../gil/extension/io/bmp/detail/write.hpp | 1 - .../gil/extension/io/jpeg/detail/read.hpp | 4 ++-- .../gil/extension/io/jpeg/detail/write.hpp | 1 - .../gil/extension/io/png/detail/read.hpp | 4 ++-- .../gil/extension/io/png/detail/write.hpp | 1 - .../gil/extension/io/pnm/detail/read.hpp | 4 ++-- .../gil/extension/io/pnm/detail/write.hpp | 1 - .../gil/extension/io/raw/detail/read.hpp | 4 ++-- .../gil/extension/io/targa/detail/read.hpp | 4 ++-- .../gil/extension/io/targa/detail/write.hpp | 1 - .../gil/extension/io/tiff/detail/read.hpp | 2 +- .../gil/extension/io/tiff/detail/write.hpp | 1 - .../dynamic.hpp} | 24 ++++++++----------- 14 files changed, 23 insertions(+), 33 deletions(-) rename include/boost/gil/io/{dynamic_io_new.hpp => detail/dynamic.hpp} (83%) diff --git a/include/boost/gil/extension/io/bmp/detail/read.hpp b/include/boost/gil/extension/io/bmp/detail/read.hpp index 136bd802b1..b898662f6e 100644 --- a/include/boost/gil/extension/io/bmp/detail/read.hpp +++ b/include/boost/gil/extension/io/bmp/detail/read.hpp @@ -11,11 +11,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -709,7 +709,7 @@ class dynamic_image_reader< Device { detail::bmp_type_format_checker format_checker( this->_info._bits_per_pixel ); - if( !construct_matched( images + if( !detail::construct_matched( images , format_checker )) { diff --git a/include/boost/gil/extension/io/bmp/detail/write.hpp b/include/boost/gil/extension/io/bmp/detail/write.hpp index fbea9ce05e..399b5f66d2 100644 --- a/include/boost/gil/extension/io/bmp/detail/write.hpp +++ b/include/boost/gil/extension/io/bmp/detail/write.hpp @@ -13,7 +13,6 @@ #include #include -#include #include diff --git a/include/boost/gil/extension/io/jpeg/detail/read.hpp b/include/boost/gil/extension/io/jpeg/detail/read.hpp index def89fd58e..7ef0b5717b 100644 --- a/include/boost/gil/extension/io/jpeg/detail/read.hpp +++ b/include/boost/gil/extension/io/jpeg/detail/read.hpp @@ -12,10 +12,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -284,7 +284,7 @@ class dynamic_image_reader< Device : JCS_RGB ); - if( !construct_matched( images + if( !detail::construct_matched( images , format_checker )) { diff --git a/include/boost/gil/extension/io/jpeg/detail/write.hpp b/include/boost/gil/extension/io/jpeg/detail/write.hpp index a41a7e6544..9baec7ead4 100644 --- a/include/boost/gil/extension/io/jpeg/detail/write.hpp +++ b/include/boost/gil/extension/io/jpeg/detail/write.hpp @@ -14,7 +14,6 @@ #include #include -#include #include diff --git a/include/boost/gil/extension/io/png/detail/read.hpp b/include/boost/gil/extension/io/png/detail/read.hpp index 06837c7bd9..62463d7557 100644 --- a/include/boost/gil/extension/io/png/detail/read.hpp +++ b/include/boost/gil/extension/io/png/detail/read.hpp @@ -13,10 +13,10 @@ #include #include // FIXME: Include what you use! +#include #include #include #include -#include #include #include #include @@ -416,7 +416,7 @@ class dynamic_image_reader< Device , this->_info._color_type ); - if( !construct_matched( images + if( !detail::construct_matched( images , format_checker )) { diff --git a/include/boost/gil/extension/io/png/detail/write.hpp b/include/boost/gil/extension/io/png/detail/write.hpp index 02c5422e68..accc639c48 100644 --- a/include/boost/gil/extension/io/png/detail/write.hpp +++ b/include/boost/gil/extension/io/png/detail/write.hpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/include/boost/gil/extension/io/pnm/detail/read.hpp b/include/boost/gil/extension/io/pnm/detail/read.hpp index fa925cfc30..1ea53c7844 100644 --- a/include/boost/gil/extension/io/pnm/detail/read.hpp +++ b/include/boost/gil/extension/io/pnm/detail/read.hpp @@ -13,11 +13,11 @@ #include #include // FIXME: Include what you use! +#include #include #include #include #include -#include #include #include #include @@ -421,7 +421,7 @@ class dynamic_image_reader< Device { detail::pnm_type_format_checker format_checker( this->_info._type ); - if( !construct_matched( images + if( !detail::construct_matched( images , format_checker )) { diff --git a/include/boost/gil/extension/io/pnm/detail/write.hpp b/include/boost/gil/extension/io/pnm/detail/write.hpp index b32c042b8c..cbce00a2e6 100644 --- a/include/boost/gil/extension/io/pnm/detail/write.hpp +++ b/include/boost/gil/extension/io/pnm/detail/write.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/include/boost/gil/extension/io/raw/detail/read.hpp b/include/boost/gil/extension/io/raw/detail/read.hpp index d0d9aefc27..af2e7eea2f 100644 --- a/include/boost/gil/extension/io/raw/detail/read.hpp +++ b/include/boost/gil/extension/io/raw/detail/read.hpp @@ -13,11 +13,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -205,7 +205,7 @@ class dynamic_image_reader< Device { detail::raw_type_format_checker format_checker( this->_info ); - if( !construct_matched( images + if( !detail::construct_matched( images , format_checker )) { diff --git a/include/boost/gil/extension/io/targa/detail/read.hpp b/include/boost/gil/extension/io/targa/detail/read.hpp index 40d42561c1..8eec904db1 100644 --- a/include/boost/gil/extension/io/targa/detail/read.hpp +++ b/include/boost/gil/extension/io/targa/detail/read.hpp @@ -12,11 +12,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -367,7 +367,7 @@ class dynamic_image_reader< Device { detail::targa_type_format_checker format_checker( this->_info._bits_per_pixel ); - if( !construct_matched( images + if( !detail::construct_matched( images , format_checker )) { diff --git a/include/boost/gil/extension/io/targa/detail/write.hpp b/include/boost/gil/extension/io/targa/detail/write.hpp index 2f2f5cc556..7c9e7f4c31 100644 --- a/include/boost/gil/extension/io/targa/detail/write.hpp +++ b/include/boost/gil/extension/io/targa/detail/write.hpp @@ -13,7 +13,6 @@ #include #include -#include #include diff --git a/include/boost/gil/extension/io/tiff/detail/read.hpp b/include/boost/gil/extension/io/tiff/detail/read.hpp index 9ce3a072f1..ccd695ff53 100644 --- a/include/boost/gil/extension/io/tiff/detail/read.hpp +++ b/include/boost/gil/extension/io/tiff/detail/read.hpp @@ -12,11 +12,11 @@ #include #include +#include #include #include #include #include -#include #include #include diff --git a/include/boost/gil/extension/io/tiff/detail/write.hpp b/include/boost/gil/extension/io/tiff/detail/write.hpp index 040a0d76f6..9425271d34 100644 --- a/include/boost/gil/extension/io/tiff/detail/write.hpp +++ b/include/boost/gil/extension/io/tiff/detail/write.hpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/include/boost/gil/io/dynamic_io_new.hpp b/include/boost/gil/io/detail/dynamic.hpp similarity index 83% rename from include/boost/gil/io/dynamic_io_new.hpp rename to include/boost/gil/io/detail/dynamic.hpp index 3f529d9013..4df5fef2e9 100644 --- a/include/boost/gil/io/dynamic_io_new.hpp +++ b/include/boost/gil/io/detail/dynamic.hpp @@ -5,8 +5,8 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#ifndef BOOST_GIL_IO_DYNAMIC_IO_NEW_HPP -#define BOOST_GIL_IO_DYNAMIC_IO_NEW_HPP +#ifndef BOOST_GIL_IO_DETAIL_DYNAMIC_HPP +#define BOOST_GIL_IO_DETAIL_DYNAMIC_HPP #include @@ -15,14 +15,12 @@ #include -namespace boost { namespace gil { - -namespace detail { +namespace boost { namespace gil { namespace detail { template struct construct_matched_t { - template + template static bool apply(any_image& img, Pred pred) { if (pred.template apply, N-1>>()) @@ -39,8 +37,8 @@ struct construct_matched_t template <> struct construct_matched_t<0> { - template - static bool apply(any_image&,Pred) { return false; } + template + static bool apply(any_image&, Pred) { return false; } }; // A function object that can be passed to apply_operation. @@ -81,24 +79,22 @@ class dynamic_io_fnobj apply(view, typename IsSupported::template apply::type()); } - template< typename View, typename Info > + template void operator()(View const& view, Info const& info) { apply(view, info, typename IsSupported::template apply::type()); } }; -} // namespace detail - /// \brief Within the any_image, constructs an image with the given dimensions /// and a type that satisfies the given predicate -template +template inline bool construct_matched(any_image& img, Pred pred) { constexpr auto size = mp11::mp_size>::value; - return detail::construct_matched_t::apply(img, pred); + return construct_matched_t::apply(img, pred); } -} } // namespace boost::gil +} } } // namespace boost::gil::detail #endif From 3289fe0bc43ff1d82770ffcb3e9268b31cfaa0d7 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sat, 30 Apr 2022 23:53:41 +0200 Subject: [PATCH 099/193] refactor: Unified operation names for pixel and channel operations (#655) Renamed - gil::pixel_multiply_t to gil::pixel_multiplies_t - gil::pixel_divide_t to gil::pixel_divides_t Closes #368 --- include/boost/gil/pixel_numeric_operations.hpp | 16 ++++++++++++---- test/core/pixel/pixel_numeric_operations.cpp | 4 ++-- .../pixel/pixel_numeric_operations_float.cpp | 6 +++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/boost/gil/pixel_numeric_operations.hpp b/include/boost/gil/pixel_numeric_operations.hpp index b501c14e8a..93104e3d91 100644 --- a/include/boost/gil/pixel_numeric_operations.hpp +++ b/include/boost/gil/pixel_numeric_operations.hpp @@ -20,8 +20,10 @@ namespace boost { namespace gil { // List of currently defined functors: // pixel_plus_t (+) // pixel_minus_t (-) -// pixel_multiplies_scalar_t (*) -// pixel_divides_scalar_t (/) +// pixel_multiplies_scalar_t (*s) +// pixel_multiplies_t (*) +// pixel_divides_scalar_t (/s) +// pixel_divides_t (/) // pixel_halves_t (/=2), // pixel_zeros_t (=0) // pixel_assigns_t (=) @@ -97,7 +99,7 @@ struct pixel_multiplies_scalar_t /// \tparam PixelRef1 - models PixelConcept /// \tparam PixelResult - models PixelValueConcept template -struct pixel_multiply_t +struct pixel_multiplies_t { auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult { @@ -113,6 +115,9 @@ struct pixel_multiply_t } }; +template +using pixel_multiply_t [[deprecated]] = pixel_multiplies_t; + /// \ingroup PixelNumericOperations /// \brief Performs channel-wise division of pixel elements by scalar. /// \tparam PixelRef - models PixelConcept @@ -139,7 +144,7 @@ struct pixel_divides_scalar_t /// \tparam PixelRef2 - models PixelConcept /// \tparam PixelResult - models PixelValueConcept template -struct pixel_divide_t +struct pixel_divides_t { auto operator()(PixelRef1 const& p1, PixelRef2 const& p2) const -> PixelResult { @@ -155,6 +160,9 @@ struct pixel_divide_t } }; +template +using pixel_divide_t [[deprecated]] = pixel_divides_t; + /// \ingroup PixelNumericOperations /// \brief Performs channel-wise division by 2 /// \tparam PixelRef - models PixelConcept diff --git a/test/core/pixel/pixel_numeric_operations.cpp b/test/core/pixel/pixel_numeric_operations.cpp index 0851b7a94b..0c74bef400 100644 --- a/test/core/pixel/pixel_numeric_operations.cpp +++ b/test/core/pixel/pixel_numeric_operations.cpp @@ -136,7 +136,7 @@ struct test_pixel_multiply_integer_same_types { using pixel_t = Pixel; using channel_t = typename gil::channel_type::type; - gil::pixel_multiply_t f; + gil::pixel_multiplies_t f; pixel_t p0; gil::static_fill(p0, static_cast(0)); @@ -190,7 +190,7 @@ struct test_pixel_divide_integer_same_types { using pixel_t = Pixel; using channel_t = typename gil::channel_type::type; - gil::pixel_divide_t f; + gil::pixel_divides_t f; pixel_t p0; gil::static_fill(p0, static_cast(0)); diff --git a/test/core/pixel/pixel_numeric_operations_float.cpp b/test/core/pixel/pixel_numeric_operations_float.cpp index dad46d65a1..da8ed8c12d 100644 --- a/test/core/pixel/pixel_numeric_operations_float.cpp +++ b/test/core/pixel/pixel_numeric_operations_float.cpp @@ -49,7 +49,7 @@ void test_multiply() gil::rgb32f_pixel_t a(1.f, 2.f, 3.f); gil::bgr32f_pixel_t b(2.f, 2.f, 2.f); - gil::pixel_multiply_t< + gil::pixel_multiplies_t< gil::rgb32f_pixel_t, gil::bgr32f_pixel_t, gil::rgb32f_pixel_t> op; gil::rgb32f_pixel_t c = op(a, b); @@ -67,7 +67,7 @@ void test_divide() gil::rgb8_pixel_t a(10, 20, 30); gil::bgr8_pixel_t b(2, 2, 2); - gil::pixel_divide_t op; + gil::pixel_divides_t op; gil::rgb32f_pixel_t c = op(a, b); BOOST_TEST_EQ(get_color(c, gil::red_t()), 5); @@ -80,7 +80,7 @@ void test_divide() gil::rgb32f_pixel_t a(1.f, 2.f, 3.f); gil::bgr32f_pixel_t b(2.f, 2.f, 2.f); - gil::pixel_divide_t op; + gil::pixel_divides_t op; gil::rgb32f_pixel_t c = op(a, b); float epsilon = 1e-6f; From c063d1c185984c49b0c3f9f400905ef2d6ed2474 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sat, 30 Apr 2022 23:57:00 +0200 Subject: [PATCH 100/193] feat: Added for_each_pixel overload for any_image (#648) Take functor by value instead of reference. Test cases to use the same fixture as the other any_image tests. Fixes #579 --- .../gil/extension/dynamic_image/algorithm.hpp | 29 +++++++++++ test/extension/dynamic_image/Jamfile | 2 + .../extension/dynamic_image/algorithm/Jamfile | 11 ++++ .../algorithm/for_each_pixel.cpp | 52 +++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 test/extension/dynamic_image/algorithm/Jamfile create mode 100644 test/extension/dynamic_image/algorithm/for_each_pixel.cpp diff --git a/include/boost/gil/extension/dynamic_image/algorithm.hpp b/include/boost/gil/extension/dynamic_image/algorithm.hpp index e63e1f9825..75a4aedb7f 100644 --- a/include/boost/gil/extension/dynamic_image/algorithm.hpp +++ b/include/boost/gil/extension/dynamic_image/algorithm.hpp @@ -230,6 +230,35 @@ void fill_pixels(any_image_view const& view, Value const& val) apply_operation(view, detail::fill_pixels_fn(val)); } +namespace detail { + +template +struct for_each_pixel_fn +{ + for_each_pixel_fn(F&& fun) : fun_(std::move(fun)) {} + + template + auto operator()(View const& view) -> F + { + return for_each_pixel(view, fun_); + } + + F fun_; +}; + +} // namespace detail + +/// \defgroup ImageViewSTLAlgorithmsForEachPixel for_each_pixel +/// \ingroup ImageViewSTLAlgorithms +/// \brief std::for_each for any image views +/// +/// \ingroup ImageViewSTLAlgorithmsForEachPixel +template +auto for_each_pixel(any_image_view const& view, F fun) -> F +{ + return variant2::visit(detail::for_each_pixel_fn(std::move(fun)), view); +} + }} // namespace boost::gil #endif diff --git a/test/extension/dynamic_image/Jamfile b/test/extension/dynamic_image/Jamfile index 63189265e4..4d78b7554b 100644 --- a/test/extension/dynamic_image/Jamfile +++ b/test/extension/dynamic_image/Jamfile @@ -13,3 +13,5 @@ alias headers : [ generate_self_contained_headers extension/dynamic_image ] ; run any_image.cpp ; run any_image_view.cpp ; run subimage_view.cpp ; + +build-project algorithm ; diff --git a/test/extension/dynamic_image/algorithm/Jamfile b/test/extension/dynamic_image/algorithm/Jamfile new file mode 100644 index 0000000000..cdca8c65e8 --- /dev/null +++ b/test/extension/dynamic_image/algorithm/Jamfile @@ -0,0 +1,11 @@ +# Boost.GIL (Generic Image Library) - tests +# +# Copyright (c) 2022 Marco Langer +# +# Distributed under 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) + +import testing ; + +run for_each_pixel.cpp ; diff --git a/test/extension/dynamic_image/algorithm/for_each_pixel.cpp b/test/extension/dynamic_image/algorithm/for_each_pixel.cpp new file mode 100644 index 0000000000..67a947cac2 --- /dev/null +++ b/test/extension/dynamic_image/algorithm/for_each_pixel.cpp @@ -0,0 +1,52 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under 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 "../test_fixture.hpp" +#include "core/image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct accumulator +{ + template + void operator()(Pixel const& p) + { + sum += gil::at_c<0>(p); + } + + int sum{}; +}; + +struct test_for_each_pixel +{ + template + void operator()(Image const&) + { + using image_t = Image; + fixture::dynamic_image image(fixture::create_image(2, 2, 128)); + accumulator acc = gil::for_each_pixel(gil::const_view(image), accumulator()); + BOOST_TEST_EQ(acc.sum, 2 * 2 * 128); + } + + static void run() + { + boost::mp11::mp_for_each(test_for_each_pixel{}); + } +}; + +int main() +{ + test_for_each_pixel::run(); + + return ::boost::report_errors(); +} \ No newline at end of file From 36a45e3af566950acc50b6ab052f3e7d7b1fc0b7 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 3 May 2022 13:42:09 +0200 Subject: [PATCH 101/193] fix documentation links in "See also" sections Fixes #591. --- doc/design/channel.rst | 10 +++++----- doc/design/color_space.rst | 6 +++--- doc/design/image.rst | 6 +++--- doc/design/image_view.rst | 14 +++++++------- doc/design/pixel.rst | 18 +++++++++--------- doc/design/pixel_iterator.rst | 16 ++++++++-------- doc/design/pixel_locator.rst | 16 ++++++++-------- doc/design/point.rst | 4 ++-- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/doc/design/channel.rst b/doc/design/channel.rst index 00f3290718..bbcc025fea 100644 --- a/doc/design/channel.rst +++ b/doc/design/channel.rst @@ -86,11 +86,11 @@ such as support for arithmetic operations. .. seealso:: - - `ChannelConcept `_ - - `ChannelValueConcept `_ - - `MutableChannelConcept `_ - - `ChannelsCompatibleConcept `_ - - `ChannelConvertibleConcept `_ + - `ChannelConcept <../reference/structboost_1_1gil_1_1_channel_concept.html>`_ + - `ChannelValueConcept <../reference/structboost_1_1gil_1_1_channel_value_concept.html>`_ + - `MutableChannelConcept <../reference/structboost_1_1gil_1_1_mutable_channel_concept.html>`_ + - `ChannelsCompatibleConcept <../reference/structboost_1_1gil_1_1_channels_compatible_concept.html>`_ + - `ChannelConvertibleConcept <../reference/structboost_1_1gil_1_1_channel_convertible_concept.html>`_ Models ------ diff --git a/doc/design/color_space.rst b/doc/design/color_space.rst index d4690e3143..7c74083eb1 100644 --- a/doc/design/color_space.rst +++ b/doc/design/color_space.rst @@ -17,9 +17,9 @@ same set of colors in the same order). .. seealso:: - - `ColorSpaceConcept `_ - - `ColorSpacesCompatibleConcept `_ - - `ChannelMappingConcept `_ + - `ColorSpaceConcept <../reference/structboost_1_1gil_1_1_color_space_concept.html>`_ + - `ColorSpacesCompatibleConcept <../reference/structboost_1_1gil_1_1_color_spaces_compatible_concept.html>`_ + - `ChannelMappingConcept <../reference/structboost_1_1gil_1_1_channel_mapping_concept.html>`_ Models ------ diff --git a/doc/design/image.rst b/doc/design/image.rst index a18baa5598..d62ac2eecd 100644 --- a/doc/design/image.rst +++ b/doc/design/image.rst @@ -73,9 +73,9 @@ because immutable images are not very useful. .. seealso:: - - `RandomAccessNDImageConcept `_ - - `RandomAccess2DImageConcept `_ - - `ImageConcept `_ + - `RandomAccessNDImageConcept <../reference/structboost_1_1gil_1_1_random_access_n_d_image_concept.html>`_ + - `RandomAccess2DImageConcept <../reference/structboost_1_1gil_1_1_random_access2_d_image_concept.html>`_ + - `ImageConcept <../reference/structboost_1_1gil_1_1_image_concept.html>`_ Models ------ diff --git a/doc/design/image_view.rst b/doc/design/image_view.rst index a1140a6730..c40d2ab859 100644 --- a/doc/design/image_view.rst +++ b/doc/design/image_view.rst @@ -151,13 +151,13 @@ compatible. .. seealso:: - - `RandomAccessNDImageViewConcept `_ - - `MutableRandomAccessNDImageViewConcept `_ - - `RandomAccess2DImageViewConcept `_ - - `MutableRandomAccess2DImageViewConcept `_ - - `ImageViewConcept `_ - - `MutableImageViewConcept `_ - - `ViewsCompatibleConcept `_ + - `RandomAccessNDImageViewConcept <../reference/structboost_1_1gil_1_1_random_access_n_d_image_view_concept.html>`_ + - `MutableRandomAccessNDImageViewConcept <../reference/structboost_1_1gil_1_1_mutable_random_access_n_d_image_view_concept.html>`_ + - `RandomAccess2DImageViewConcept <../reference/structboost_1_1gil_1_1_random_access2_d_image_view_concept.html>`_ + - `MutableRandomAccess2DImageViewConcept <../reference/structboost_1_1gil_1_1_mutable_random_access2_d_image_view_concept.html>`_ + - `ImageViewConcept <../reference/structboost_1_1gil_1_1_image_view_concept.html>`_ + - `MutableImageViewConcept <../reference/structboost_1_1gil_1_1_mutable_image_view_concept.html>`_ + - `ViewsCompatibleConcept <../reference/structboost_1_1gil_1_1_views_compatible_concept.html>`_ Models ------ diff --git a/doc/design/pixel.rst b/doc/design/pixel.rst index 8f18fec14e..1f8478ff1c 100644 --- a/doc/design/pixel.rst +++ b/doc/design/pixel.rst @@ -119,15 +119,15 @@ both, but only pixel values model the latter. .. seealso:: - - `PixelBasedConcept

`_ - - `PixelConcept `_ - - `MutablePixelConcept `_ - - `PixelValueConcept `_ - - `HomogeneousPixelConcept `_ - - `MutableHomogeneousPixelConcept `_ - - `HomogeneousPixelValueConcept `_ - - `PixelsCompatibleConcept `_ - - `PixelConvertibleConcept `_ + - `PixelBasedConcept

<../reference/structboost_1_1gil_1_1_pixel_based_concept.html>`_ + - `PixelConcept <../reference/structboost_1_1gil_1_1_pixel_concept.html>`_ + - `MutablePixelConcept <../reference/structboost_1_1gil_1_1_mutable_pixel_concept.html>`_ + - `PixelValueConcept <../reference/structboost_1_1gil_1_1_pixel_value_concept.html>`_ + - `HomogeneousPixelConcept <../reference/structboost_1_1gil_1_1_homogeneous_pixel_based_concept.html>`_ + - `MutableHomogeneousPixelConcept <../reference/structboost_1_1gil_1_1_mutable_homogeneous_pixel_concept.html>`_ + - `HomogeneousPixelValueConcept <../reference/structboost_1_1gil_1_1_homogeneous_pixel_value_concept.html>`_ + - `PixelsCompatibleConcept <../reference/structboost_1_1gil_1_1_pixels_compatible_concept.html>`_ + - `PixelConvertibleConcept <../reference/structboost_1_1gil_1_1_pixel_convertible_concept.html>`_ Models ------ diff --git a/doc/design/pixel_iterator.rst b/doc/design/pixel_iterator.rst index 35fde992c3..1bd2c73232 100644 --- a/doc/design/pixel_iterator.rst +++ b/doc/design/pixel_iterator.rst @@ -36,8 +36,8 @@ plain iterators or adaptors over another pixel iterator: .. seealso:: - - `PixelIteratorConcept `_ - - `MutablePixelIteratorConcept `_ + - `PixelIteratorConcept <../reference/group___pixel_iterator_concept_pixel_iterator.html>`_ + - `MutablePixelIteratorConcept <../reference/structboost_1_1gil_1_1_mutable_pixel_iterator_concept.html>`_ Models ^^^^^^ @@ -120,8 +120,8 @@ type, and a metafunction to rebind to another base iterator: .. seealso:: - - `IteratorAdaptorConcept `_ - - `MutableIteratorAdaptorConcept `_ + - `IteratorAdaptorConcept <../reference/structboost_1_1gil_1_1_iterator_adaptor_concept.html>`_ + - `MutableIteratorAdaptorConcept <../reference/structboost_1_1gil_1_1_mutable_iterator_adaptor_concept.html>`_ Models ^^^^^^ @@ -262,10 +262,10 @@ support ``HasDynamicXStepTypeConcept``. .. seealso:: - - `StepIteratorConcept `_ - - `MutableStepIteratorConcept `_ - - `MemoryBasedIteratorConcept `_ - - `HasDynamicXStepTypeConcept `_ + - `StepIteratorConcept <../reference/structboost_1_1gil_1_1_step_iterator_concept.html>`_ + - `MutableStepIteratorConcept <../reference/structboost_1_1gil_1_1_mutable_step_iterator_concept.html>`_ + - `MemoryBasedIteratorConcept <../reference/structboost_1_1gil_1_1_memory_based_iterator_concept.html>`_ + - `HasDynamicXStepTypeConcept <../reference/structboost_1_1gil_1_1_has_dynamic_x_step_type_concept.html>`_ Models ^^^^^^ diff --git a/doc/design/pixel_locator.rst b/doc/design/pixel_locator.rst index 2d9d5ace8b..dfad531e90 100644 --- a/doc/design/pixel_locator.rst +++ b/doc/design/pixel_locator.rst @@ -159,14 +159,14 @@ y dimension types are the same. They model the following concept: .. seealso:: - - `HasDynamicYStepTypeConcept `_ - - `HasTransposedTypeConcept `_ - - `RandomAccessNDLocatorConcept `_ - - `MutableRandomAccessNDLocatorConcept `_ - - `RandomAccess2DLocatorConcept `_ - - `MutableRandomAccess2DLocatorConcept `_ - - `PixelLocatorConcept `_ - - `MutablePixelLocatorConcept `_ + - `HasDynamicYStepTypeConcept <../reference/structboost_1_1gil_1_1_has_dynamic_y_step_type_concept.html>`_ + - `HasTransposedTypeConcept <../reference/structboost_1_1gil_1_1_has_transposed_type_concept.html>`_ + - `RandomAccessNDLocatorConcept <../reference/structboost_1_1gil_1_1_random_access_n_d_locator_concept.html>`_ + - `MutableRandomAccessNDLocatorConcept <../reference/structboost_1_1gil_1_1_mutable_random_access_n_d_locator_concept.html>`_ + - `RandomAccess2DLocatorConcept <../reference/structboost_1_1gil_1_1_random_access2_d_locator_concept.html>`_ + - `MutableRandomAccess2DLocatorConcept <../reference/structboost_1_1gil_1_1_mutable_random_access2_d_locator_concept.html>`_ + - `PixelLocatorConcept <../reference/structboost_1_1gil_1_1_pixel_locator_concept.html>`_ + - `MutablePixelLocatorConcept <../reference/structboost_1_1gil_1_1_mutable_pixel_locator_concept.html>`_ Models ------ diff --git a/doc/design/point.rst b/doc/design/point.rst index e32bca936e..28158ab2db 100644 --- a/doc/design/point.rst +++ b/doc/design/point.rst @@ -46,8 +46,8 @@ in which both dimensions are of the same type: .. seealso:: - - `PointNDConcept `_ - - `Point2DConcept `_ + - `PointNDConcept <../reference/structboost_1_1gil_1_1_point_n_d_concept.html>`_ + - `Point2DConcept <../reference/structboost_1_1gil_1_1_point2_d_concept.html>`_ Models ------ From 9666a95bc5e0bf2a8780c307c945a8452473d3b3 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Thu, 12 May 2022 14:23:49 +0200 Subject: [PATCH 102/193] chore: Remove unused variable in diffusion.hpp (#666) The variable `dims` is not used and causes a compiler warning. --- include/boost/gil/image_processing/diffusion.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/boost/gil/image_processing/diffusion.hpp b/include/boost/gil/image_processing/diffusion.hpp index 2a2edb097d..fb1c86a4d3 100644 --- a/include/boost/gil/image_processing/diffusion.hpp +++ b/include/boost/gil/image_processing/diffusion.hpp @@ -370,7 +370,6 @@ void anisotropic_diffusion(const InputView& input, const OutputView& output, uns using computation_image = image; const auto width = input.width(); const auto height = input.height(); - const point_t dims(width, height); const auto zero_pixel = []() { pixel_type pixel; static_fill(pixel, static_cast(0)); From f839504f2081321509333c28eb673e437c5218a2 Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Fri, 13 May 2022 18:13:00 +0600 Subject: [PATCH 103/193] Fixed io extension build error (#671) The error was due to missing dynamic.hpp (renamed in #653) header that contained dynamic_io_fnobj. Adding the header back in to write.hpp headers of io extensions fixed the problem. --- include/boost/gil/extension/io/bmp/detail/write.hpp | 1 + include/boost/gil/extension/io/jpeg/detail/write.hpp | 1 + include/boost/gil/extension/io/png/detail/write.hpp | 1 + include/boost/gil/extension/io/pnm/detail/write.hpp | 1 + include/boost/gil/extension/io/targa/detail/write.hpp | 1 + include/boost/gil/extension/io/tiff/detail/write.hpp | 1 + 6 files changed, 6 insertions(+) diff --git a/include/boost/gil/extension/io/bmp/detail/write.hpp b/include/boost/gil/extension/io/bmp/detail/write.hpp index 399b5f66d2..d82e1f2844 100644 --- a/include/boost/gil/extension/io/bmp/detail/write.hpp +++ b/include/boost/gil/extension/io/bmp/detail/write.hpp @@ -13,6 +13,7 @@ #include #include +#include #include diff --git a/include/boost/gil/extension/io/jpeg/detail/write.hpp b/include/boost/gil/extension/io/jpeg/detail/write.hpp index 9baec7ead4..e3705616cf 100644 --- a/include/boost/gil/extension/io/jpeg/detail/write.hpp +++ b/include/boost/gil/extension/io/jpeg/detail/write.hpp @@ -14,6 +14,7 @@ #include #include +#include #include diff --git a/include/boost/gil/extension/io/png/detail/write.hpp b/include/boost/gil/extension/io/png/detail/write.hpp index accc639c48..f5cdf1cb67 100644 --- a/include/boost/gil/extension/io/png/detail/write.hpp +++ b/include/boost/gil/extension/io/png/detail/write.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include diff --git a/include/boost/gil/extension/io/pnm/detail/write.hpp b/include/boost/gil/extension/io/pnm/detail/write.hpp index cbce00a2e6..c6598ace45 100644 --- a/include/boost/gil/extension/io/pnm/detail/write.hpp +++ b/include/boost/gil/extension/io/pnm/detail/write.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/include/boost/gil/extension/io/targa/detail/write.hpp b/include/boost/gil/extension/io/targa/detail/write.hpp index 7c9e7f4c31..23d96bedad 100644 --- a/include/boost/gil/extension/io/targa/detail/write.hpp +++ b/include/boost/gil/extension/io/targa/detail/write.hpp @@ -13,6 +13,7 @@ #include #include +#include #include diff --git a/include/boost/gil/extension/io/tiff/detail/write.hpp b/include/boost/gil/extension/io/tiff/detail/write.hpp index 9425271d34..cd83a26ff6 100644 --- a/include/boost/gil/extension/io/tiff/detail/write.hpp +++ b/include/boost/gil/extension/io/tiff/detail/write.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include From ec9f0b0240ae0b264ac59cae112199b62948d3d3 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Fri, 13 May 2022 23:06:14 +0200 Subject: [PATCH 104/193] docs: Add partial release notes for Boost 1.80 (#670) Includes changes up to commit 25 from the table shown in . --- RELEASES.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index d006546324..bb08d5660b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,6 +3,31 @@ All notable changes to [Boost.GIL](https://github.com/boostorg/gil/) project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.80.0] - 2022-08-10 + +### Added +- Added `image` constructor from compatible view ([PR #520](https://github.com/boostorg/gil/pull/520)) +- Added inverse function for affine `matrix3x2` ([PR #527](https://github.com/boostorg/gil/pull/527)) +- Added Perona-Malik anisotropic diffusion algorithm [[PR #500](https://github.com/boostorg/gil/pull/500)) +- GSoC 2020: Add histogram class and related functionality ([PR #499](https://github.com/boostorg/gil/pull/499)) +- GSoC 2020: Add histogram equalization feature ([PR #514](https://github.com/boostorg/gil/pull/514)) +- GSoC 2020: Add histogram matching algorithm ([PR #515](https://github.com/boostorg/gil/pull/515)) +- Added ability to stack images either horizontally (`hstack`) or vertically (`vstack`) ([PR #506](https://github.com/boostorg/gil/pull/506)) +- Added adaptive histogram equalization algorithm ([PR #516](https://github.com/boostorg/gil/pull/516)) +- Added implementation of Hough transformations ([PR #512](https://github.com/boostorg/gil/pull/512)) + +### Changed +- documentation: Display that GIL is a header-only library + +### Fixed +- Fixed conversion from RGB to signed CMYK ([PR #522](https://github.com/boostorg/gil/pull/522)) +- Removed unnecessary numeric cast in hsv.hpp ([PR #530](https://github.com/boostorg/gil/pull/530)) +- Fixed default constructor for `homogeneous_color_base` for reference pixel elements ([PR #542](https://github.com/boostorg/gil/pull/542)) + +### Acknowledgements + +Samuel Debionne, Mateusz Łoskot, Debabrata Mandal, Harshit Pant, Olzhas Zhumabek + ## [1.75.0] - 2020-12-09 BREAKING: In next release, we are going to drop support for GCC 5. We may also change the required minimum C++ version from C++11 to C++14. From fb8e389ada1e7ef536f851fd69100a2be72625ee Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Sat, 14 May 2022 22:52:27 +0200 Subject: [PATCH 105/193] docs: Add remaining part of release notes for Boost 1.80 (#672) Includes changes up to commit 106 (hash 36a45e3af) from the table shown in . Those are all commits form the grand merge. [ci skip] --- RELEASES.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index bb08d5660b..4811719034 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -15,18 +15,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added ability to stack images either horizontally (`hstack`) or vertically (`vstack`) ([PR #506](https://github.com/boostorg/gil/pull/506)) - Added adaptive histogram equalization algorithm ([PR #516](https://github.com/boostorg/gil/pull/516)) - Added implementation of Hough transformations ([PR #512](https://github.com/boostorg/gil/pull/512)) +- Added standard morphological transformations ([PR #541](https://github.com/boostorg/gil/pull/541)) +- Added rotation of image by arbitrary angle around its center ([PR #565](https://github.com/boostorg/gil/pull/565)) +- Added rasterization support for ellipse ([PR #585](https://github.com/boostorg/gil/pull/585)) +- Added `for_each_pixel` overload for `any_image` ([PR #648](https://github.com/boostorg/gil/pull/648)) ### Changed - documentation: Display that GIL is a header-only library +- Moved numeric extension to core ([PR #573](https://github.com/boostorg/gil/pull/573)) +- Added support for C++17's `` ([PR #636](https://github.com/boostorg/gil/pull/636)). + The availability of the ``std::filesystem`` is detected automatically, + unless the `BOOST_GIL_IO_USE_BOOST_FILESYSTEM` macro is defined that forces + the preference of the Boost.Filesystem. +- Renamed `pixel_multiply_t` to `pixel_multiplies_t` and `pixel_divide_t` to `pixel_divides_t`([PR #655](https://github.com/boostorg/gil/pull/655)) + +### Removed +- BREAKING: Removed support for GCC 5 ([PR #572](https://github.com/boostorg/gil/pull/572)) +- Removed deprecated.hpp ([PR #627](https://github.com/boostorg/gil/pull/627)) ### Fixed - Fixed conversion from RGB to signed CMYK ([PR #522](https://github.com/boostorg/gil/pull/522)) - Removed unnecessary numeric cast in hsv.hpp ([PR #530](https://github.com/boostorg/gil/pull/530)) - Fixed default constructor for `homogeneous_color_base` for reference pixel elements ([PR #542](https://github.com/boostorg/gil/pull/542)) +- Fixed returning reference to local temporary object in `subchroma_image_view` ([PR #556](https://github.com/boostorg/gil/pull/556)) +- Added missing header guards in diffusion.hpp ([PR #568](https://github.com/boostorg/gil/pull/568)) +- Fixed `any_image_view<>::const_t` ([PR #526](https://github.com/boostorg/gil/pull/526)) +- Fixed C++20 incompatibilities in I/O extensions ([PR #617](https://github.com/boostorg/gil/pull/617)) +- Ensured all examples build without errors ([PR #628](https://github.com/boostorg/gil/pull/628)) +- Fixed `convolve_2d` for images with `float32_t` channel model ([PR #577](https://github.com/boostorg/gil/pull/577)) +- Fixed `for_each_pixel` for non-1d iterable views ([PR #621](https://github.com/boostorg/gil/pull/621)) +- Fixed: `is_equal_to_sixteen` in PNG I/O was less-than test ([PR #650](https://github.com/boostorg/gil/pull/650)) ### Acknowledgements -Samuel Debionne, Mateusz Łoskot, Debabrata Mandal, Harshit Pant, Olzhas Zhumabek +Samuel Debionne, Nicolas Herry, Gaurav Kumar, Marco Langer, Pranam Lashkari, Mateusz Łoskot, Debabrata Mandal, Felix Morgner, Harshit Pant, Dirk Stolle, Prathamesh Tagore, Olzhas Zhumabek ## [1.75.0] - 2020-12-09 From 8caa148e0a1e2e6d0fdba18d7029e1b9ab6331ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 17 May 2022 21:34:29 +0200 Subject: [PATCH 106/193] docs: Added more release notes with GSoC work [ci skip] --- RELEASES.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 4811719034..b8acfa5027 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -14,10 +14,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - GSoC 2020: Add histogram matching algorithm ([PR #515](https://github.com/boostorg/gil/pull/515)) - Added ability to stack images either horizontally (`hstack`) or vertically (`vstack`) ([PR #506](https://github.com/boostorg/gil/pull/506)) - Added adaptive histogram equalization algorithm ([PR #516](https://github.com/boostorg/gil/pull/516)) -- Added implementation of Hough transformations ([PR #512](https://github.com/boostorg/gil/pull/512)) +- Added Standard Hough Transform and circle rasterization ([PR #512](https://github.com/boostorg/gil/pull/512)) +- Added Bresenham's algorithm for line rasterization ([PR #512](https://github.com/boostorg/gil/pull/512)) - Added standard morphological transformations ([PR #541](https://github.com/boostorg/gil/pull/541)) - Added rotation of image by arbitrary angle around its center ([PR #565](https://github.com/boostorg/gil/pull/565)) -- Added rasterization support for ellipse ([PR #585](https://github.com/boostorg/gil/pull/585)) +- Added rasterization support for ellipse based on "An Efficient Ellipse-Drawing Algorithm" by Jerry Van Aken ([PR #585](https://github.com/boostorg/gil/pull/585)) - Added `for_each_pixel` overload for `any_image` ([PR #648](https://github.com/boostorg/gil/pull/648)) ### Changed From 44d4ab8d4b9c35e2aa2d879492d83bc820311328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 18 May 2022 09:24:35 +0200 Subject: [PATCH 107/193] ci: Disable g++-8 cxxstd=17 - segmentation fault for simple_all_formats test Mystery: The C++17 is the only build using GCC 8 for which simple_all_formats test fails with segmentation fault. Something to look at in future. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9aeb73898b..db59d4b61a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: os: ubuntu-18.04 - toolset: gcc compiler: g++-8 - cxxstd: "11,14,17" + cxxstd: "11,14" os: ubuntu-18.04 install: g++-8 - toolset: gcc-9 From 3090f866f25ccc44772b55f4e3986ec0ab3617d5 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Wed, 18 May 2022 09:30:06 +0200 Subject: [PATCH 108/193] fix: Re-allow devicen_t with two color components (#654) devicen_t<2> was possible in Boost 1.71, but broke in Boost 1.72. The static_assert was introduced in commit 5611bd58071873d6346fe3cc05fe86c72f697717 in PR #274 Now it's possible to use it again. Fixes #519 --- include/boost/gil/device_n.hpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/boost/gil/device_n.hpp b/include/boost/gil/device_n.hpp index 6868d66466..b025d87cdf 100644 --- a/include/boost/gil/device_n.hpp +++ b/include/boost/gil/device_n.hpp @@ -32,8 +32,8 @@ struct devicen_color_t {}; template struct devicen_t; -/// \brief Unnamed color space of 1, 3, 4, or 5 channels -/// \tparam N Number of color components (1, 3, 4 or 5). +/// \brief Unnamed color space of 1, 2, 3, 4, or 5 channels +/// \tparam N Number of color components (1, 2, 3, 4 or 5). /// \ingroup ColorSpaceModel template struct devicen_t @@ -43,7 +43,7 @@ struct devicen_t using color_t = devicen_color_t; static_assert( - N == 1 || (3 <= N && N <= 5), + (1 <= N && N <= 5), "invalid number of DeviceN color components"); public: @@ -58,8 +58,9 @@ struct devicen_layout_t : layout::type> {}; /// \ingroup ImageViewConstructors /// \brief from 2-channel planar data template -inline typename type_from_x_iterator>>::view_t -planar_devicen_view(std::size_t width, std::size_t height, IC c0, IC c1, std::ptrdiff_t rowsize_in_bytes) +inline +auto planar_devicen_view(std::size_t width, std::size_t height, IC c0, IC c1, std::ptrdiff_t rowsize_in_bytes) + -> typename type_from_x_iterator>>::view_t { using view_t = typename type_from_x_iterator>>::view_t; return view_t(width, height, typename view_t::locator(typename view_t::x_iterator(c0,c1), rowsize_in_bytes)); From 1be8c87c1047733aeeb3c29585ebafb057753356 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Wed, 18 May 2022 23:25:36 +0200 Subject: [PATCH 109/193] chore: do not use deprecated header in test or elsewhere (#675) --- example/convolution.cpp | 2 +- include/boost/gil/extension/numeric/kernel.hpp | 2 +- test/core/channel/channel_numeric_operations.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example/convolution.cpp b/example/convolution.cpp index 52c27b5d0f..b7e1b6b2e3 100644 --- a/example/convolution.cpp +++ b/example/convolution.cpp @@ -19,7 +19,7 @@ // Work can be done row by row and column by column, as in this example, // using the functions convolve_rows and convolve_cols (or their _fixed counterpart) -// but the header boost/gil/extension/numeric/convolve.hpp also offers the function convolve_1d which combines the two. +// but the header boost/gil/image_processing/convolve.hpp also offers the function convolve_1d which combines the two. // See also: // convolve2d.cpp - Convolution with 2d kernels diff --git a/include/boost/gil/extension/numeric/kernel.hpp b/include/boost/gil/extension/numeric/kernel.hpp index 103df047af..1f0299baae 100644 --- a/include/boost/gil/extension/numeric/kernel.hpp +++ b/include/boost/gil/extension/numeric/kernel.hpp @@ -13,6 +13,6 @@ #include -BOOST_HEADER_DEPRECATED("") +BOOST_HEADER_DEPRECATED("") #endif diff --git a/test/core/channel/channel_numeric_operations.cpp b/test/core/channel/channel_numeric_operations.cpp index 9b4d4fccdc..ff20b35b92 100644 --- a/test/core/channel/channel_numeric_operations.cpp +++ b/test/core/channel/channel_numeric_operations.cpp @@ -7,7 +7,7 @@ // http://www.boost.org/LICENSE_1_0.txt // #include -#include +#include #include From d64e70dc84f48c0f0db8ae815599d87f0d38d856 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Wed, 18 May 2022 23:26:41 +0200 Subject: [PATCH 110/193] docs: add release notes for issue #654 (#674) [ci skip] --- RELEASES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index b8acfa5027..51ccf409ce 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -46,6 +46,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fixed `convolve_2d` for images with `float32_t` channel model ([PR #577](https://github.com/boostorg/gil/pull/577)) - Fixed `for_each_pixel` for non-1d iterable views ([PR #621](https://github.com/boostorg/gil/pull/621)) - Fixed: `is_equal_to_sixteen` in PNG I/O was less-than test ([PR #650](https://github.com/boostorg/gil/pull/650)) +- Re-allow `devicen_t` with two components ([PR #654](https://github.com/boostorg/gil/pull/654)). + It was unintentionally removed in Boost 1.72. ### Acknowledgements From 4ad824e8dd379632809cf0b57478252b985fb5cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 18 May 2022 23:31:04 +0200 Subject: [PATCH 111/193] ci: Remove clang 3.5 through 3.8 jobs based on Ubuntu 16.04 Some of those clang jobs will be restored once we refactor our GHA workflows with builds --- .github/workflows/ci.yml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db59d4b61a..583d8107a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,29 +36,6 @@ jobs: - toolset: gcc-10 cxxstd: "11,14,17" os: ubuntu-18.04 - - toolset: clang - compiler: clang++-3.5 - cxxstd: "11" - define: "_GLIBCXX_USE_CXX11_ABI=0" - os: ubuntu-16.04 - install: clang-3.5 - - toolset: clang - compiler: clang++-3.6 - cxxstd: "11,14" - define: "_GLIBCXX_USE_CXX11_ABI=0" - os: ubuntu-16.04 - install: clang-3.6 - - toolset: clang - compiler: clang++-3.7 - cxxstd: "11,14" - define: "_GLIBCXX_USE_CXX11_ABI=0" - os: ubuntu-16.04 - install: clang-3.7 - - toolset: clang - compiler: clang++-3.8 - cxxstd: "11,14" - os: ubuntu-16.04 - install: clang-3.8 - toolset: clang compiler: clang++-3.9 cxxstd: "11,14" From dcae92f5c2b6b9523544da5b672c7f6e40c15541 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 24 May 2022 09:27:31 +0200 Subject: [PATCH 112/193] docs!: announce switch from C++11 to C++14 with Boost 1.80 (#677) See https://github.com/boostorg/gil/discussions/676 for the discussion to move to a newer standard. [ci skip] --- RELEASES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 51ccf409ce..296a8c9c1b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -22,6 +22,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added `for_each_pixel` overload for `any_image` ([PR #648](https://github.com/boostorg/gil/pull/648)) ### Changed +- BREAKING: The required minimum C++ version is changed from from C++11 to C++14. + Currently, large parts of GIL still compile with a C++11 compiler. However, + there is no guarantee that it stays that way, and any compilers that do not + support at least C++14 are considered unsupported as of now. - documentation: Display that GIL is a header-only library - Moved numeric extension to core ([PR #573](https://github.com/boostorg/gil/pull/573)) - Added support for C++17's `` ([PR #636](https://github.com/boostorg/gil/pull/636)). From 6d312af51acfc922a61a635d3da90b9acd45e62b Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Thu, 26 May 2022 08:58:19 +0200 Subject: [PATCH 113/193] chore!: deprecate any_color_converted_view (#678) color_converted_view has the same implementation and can be used as a replacement, providing the same functionality. This builds on #660 by @marco-langer (Thank you!) Due to the fact that the switch to C++14 has been announced in #677, we can now officially use the [[deprecated]] attribute which was standardized in C++14. This is initial part of deprecating the `any_color_converted_view` and as @marco-langer pointed in https://github.com/boostorg/gil/pull/660#issuecomment-1138183545 there are more changes to follow. --- RELEASES.md | 2 ++ .../boost/gil/extension/dynamic_image/image_view_factory.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 296a8c9c1b..fc12f0c8d2 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -26,6 +26,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Currently, large parts of GIL still compile with a C++11 compiler. However, there is no guarantee that it stays that way, and any compilers that do not support at least C++14 are considered unsupported as of now. +- BREAKING: `any_color_converted_view()` is deprecated and will be removed in the next release. + Use `color_converted_view()` instead, which provides the same feature. - documentation: Display that GIL is a header-only library - Moved numeric extension to core ([PR #573](https://github.com/boostorg/gil/pull/573)) - Added support for C++17's `` ([PR #636](https://github.com/boostorg/gil/pull/636)). diff --git a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp index fe88222a14..1418f162f1 100644 --- a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp +++ b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp @@ -379,6 +379,7 @@ auto color_converted_view(any_image_view const& src) /// These are workarounds for GCC 3.4, which thinks color_converted_view is ambiguous with the same method for templated views (in gil/image_view_factory.hpp) /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template +[[deprecated("Use color_converted_view(const any_image_view& src, CC) instead.")]] inline auto any_color_converted_view(const any_image_view& src, CC) -> typename color_converted_view_type, DstP, CC>::type @@ -392,6 +393,7 @@ auto any_color_converted_view(const any_image_view& src, CC) /// These are workarounds for GCC 3.4, which thinks color_converted_view is ambiguous with the same method for templated views (in gil/image_view_factory.hpp) /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template +[[deprecated("Use color_converted_view(any_image_view const& src) instead.")]] inline auto any_color_converted_view(const any_image_view& src) -> typename color_converted_view_type, DstP>::type From 98f49711dd8b4b5cff74d7feddbdaacb94943c54 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sat, 28 May 2022 11:07:54 +0200 Subject: [PATCH 114/193] refactor: Make packed_pixel trivially copyable and assignable (#679) --- include/boost/gil/packed_pixel.hpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/boost/gil/packed_pixel.hpp b/include/boost/gil/packed_pixel.hpp index 32dfae614f..ff1e123c9b 100644 --- a/include/boost/gil/packed_pixel.hpp +++ b/include/boost/gil/packed_pixel.hpp @@ -61,9 +61,6 @@ struct packed_pixel packed_pixel() = default; explicit packed_pixel(const BitField& bitfield) : _bitfield(bitfield) {} - // Construct from another compatible pixel type - packed_pixel(const packed_pixel& p) : _bitfield(p._bitfield) {} - template packed_pixel(Pixel const& p, typename std::enable_if::value>::type* /*dummy*/ = nullptr) @@ -110,12 +107,6 @@ struct packed_pixel gil::at_c<4>(*this) = chan4; } - auto operator=(packed_pixel const& p) -> packed_pixel& - { - _bitfield = p._bitfield; - return *this; - } - template auto operator=(Pixel const& p) -> packed_pixel& { From bab2a370ff09b4f526cd8e5084c5435208b8a799 Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Tue, 17 May 2022 23:28:32 +0600 Subject: [PATCH 115/193] Fix Hough transform and move rasterization Hough line transform had incorrect rounding which lead to misleading message --- CMakeLists.txt | 1 + example/hough_transform_line.cpp | 5 ++++- include/boost/gil.hpp | 6 +++--- include/boost/gil/{ => extension}/rasterization/circle.hpp | 4 ++-- include/boost/gil/{ => extension}/rasterization/ellipse.hpp | 0 include/boost/gil/{ => extension}/rasterization/line.hpp | 2 +- include/boost/gil/image_processing/hough_transform.hpp | 2 +- test/core/CMakeLists.txt | 3 +-- test/extension/CMakeLists.txt | 4 ++++ test/{core => extension}/rasterization/CMakeLists.txt | 0 test/{core => extension}/rasterization/Jamfile | 0 test/{core => extension}/rasterization/circle.cpp | 2 +- test/{core => extension}/rasterization/ellipse.cpp | 2 +- test/{core => extension}/rasterization/line.cpp | 4 ++-- 14 files changed, 21 insertions(+), 14 deletions(-) rename include/boost/gil/{ => extension}/rasterization/circle.hpp (98%) rename include/boost/gil/{ => extension}/rasterization/ellipse.hpp (100%) rename include/boost/gil/{ => extension}/rasterization/line.hpp (99%) rename test/{core => extension}/rasterization/CMakeLists.txt (100%) rename test/{core => extension}/rasterization/Jamfile (100%) rename test/{core => extension}/rasterization/circle.cpp (98%) rename test/{core => extension}/rasterization/ellipse.cpp (99%) rename test/{core => extension}/rasterization/line.cpp (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5de286f35d..a492c35066 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ option(BOOST_GIL_ENABLE_EXT_DYNAMIC_IMAGE "Enable Dynamic Image extension, tests option(BOOST_GIL_ENABLE_EXT_IO "Enable IO extension, tests and examples (require libjpeg, libpng, libtiff)" ON) option(BOOST_GIL_ENABLE_EXT_NUMERIC "Enable Numeric extension, tests and examples" ON) option(BOOST_GIL_ENABLE_EXT_TOOLBOX "Enable Toolbox extension, tests and examples" ON) +option(BOOST_GIL_ENABLE_EXT_RASTERIZATION "Enable Rasterization extension and tests" ON) option(BOOST_GIL_USE_CONAN "Use Conan to install dependencies" OFF) option(BOOST_GIL_USE_CLANG_TIDY "Set CMAKE_CXX_CLANG_TIDY property on targets to enable clang-tidy linting" OFF) set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard version to use (default is 11)") diff --git a/example/hough_transform_line.cpp b/example/hough_transform_line.cpp index 75c9e611b6..0d9a14f943 100644 --- a/example/hough_transform_line.cpp +++ b/example/hough_transform_line.cpp @@ -52,7 +52,7 @@ int main() double _5_degrees = gil::detail::pi / 36; auto theta_parameter = gil::make_theta_parameter(_45_degrees, _5_degrees, input_view.dimensions()); - auto expected_radius = static_cast(std::round(std::cos(_45_degrees) * size)); + auto expected_radius = static_cast(std::floor(std::cos(_45_degrees) * size)); auto radius_parameter = gil::hough_parameter::from_step_size(expected_radius, 7, 1); gil::gray32_image_t accumulator_array_image(theta_parameter.step_count, @@ -70,6 +70,9 @@ int main() theta_parameter.start_point + theta_index * theta_parameter.step_size; std::ptrdiff_t current_radius = radius_parameter.start_point + radius_parameter.step_size * radius_index; + if (current_theta == _45_degrees && current_radius == expected_radius) { + std::cout << "* "; + } std::cout << "theta: " << current_theta << " radius: " << current_radius << " accumulated value: " << accumulator_array(theta_index, radius_index)[0] << '\n'; diff --git a/include/boost/gil.hpp b/include/boost/gil.hpp index 087c9fe8de..16088a0978 100644 --- a/include/boost/gil.hpp +++ b/include/boost/gil.hpp @@ -39,9 +39,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/include/boost/gil/rasterization/circle.hpp b/include/boost/gil/extension/rasterization/circle.hpp similarity index 98% rename from include/boost/gil/rasterization/circle.hpp rename to include/boost/gil/extension/rasterization/circle.hpp index 31c3cf6caf..b91650f673 100644 --- a/include/boost/gil/rasterization/circle.hpp +++ b/include/boost/gil/extension/rasterization/circle.hpp @@ -9,8 +9,8 @@ #ifndef BOOST_GIL_RASTERIZATION_CIRCLE_HPP #define BOOST_GIL_RASTERIZATION_CIRCLE_HPP -#include -#include +#include "boost/gil/detail/math.hpp" +#include "boost/gil/point.hpp" #include #include diff --git a/include/boost/gil/rasterization/ellipse.hpp b/include/boost/gil/extension/rasterization/ellipse.hpp similarity index 100% rename from include/boost/gil/rasterization/ellipse.hpp rename to include/boost/gil/extension/rasterization/ellipse.hpp diff --git a/include/boost/gil/rasterization/line.hpp b/include/boost/gil/extension/rasterization/line.hpp similarity index 99% rename from include/boost/gil/rasterization/line.hpp rename to include/boost/gil/extension/rasterization/line.hpp index 1ff91b6a35..79ee5029e8 100644 --- a/include/boost/gil/rasterization/line.hpp +++ b/include/boost/gil/extension/rasterization/line.hpp @@ -7,7 +7,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#include +#include "boost/gil/point.hpp" #include #include diff --git a/include/boost/gil/image_processing/hough_transform.hpp b/include/boost/gil/image_processing/hough_transform.hpp index 982c28c1f9..1de81f0768 100644 --- a/include/boost/gil/image_processing/hough_transform.hpp +++ b/include/boost/gil/image_processing/hough_transform.hpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 2c812d014b..761632decd 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -39,5 +39,4 @@ add_subdirectory(image) add_subdirectory(image_view) add_subdirectory(algorithm) add_subdirectory(image_processing) -add_subdirectory(histogram) -add_subdirectory(rasterization) +add_subdirectory(histogram) \ No newline at end of file diff --git a/test/extension/CMakeLists.txt b/test/extension/CMakeLists.txt index 9f7883066f..2d4c9c578a 100644 --- a/test/extension/CMakeLists.txt +++ b/test/extension/CMakeLists.txt @@ -5,6 +5,10 @@ # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # +if(BOOST_GIL_ENABLE_EXT_RASTERIZATION) + add_subdirectory(rasterization) +endif() + if(BOOST_GIL_ENABLE_EXT_DYNAMIC_IMAGE) add_subdirectory(dynamic_image) endif() diff --git a/test/core/rasterization/CMakeLists.txt b/test/extension/rasterization/CMakeLists.txt similarity index 100% rename from test/core/rasterization/CMakeLists.txt rename to test/extension/rasterization/CMakeLists.txt diff --git a/test/core/rasterization/Jamfile b/test/extension/rasterization/Jamfile similarity index 100% rename from test/core/rasterization/Jamfile rename to test/extension/rasterization/Jamfile diff --git a/test/core/rasterization/circle.cpp b/test/extension/rasterization/circle.cpp similarity index 98% rename from test/core/rasterization/circle.cpp rename to test/extension/rasterization/circle.cpp index b4c85c7da4..33af44c491 100644 --- a/test/core/rasterization/circle.cpp +++ b/test/extension/rasterization/circle.cpp @@ -8,7 +8,7 @@ // #include -#include +#include "boost/gil/extension/rasterization/circle.hpp" #include #include diff --git a/test/core/rasterization/ellipse.cpp b/test/extension/rasterization/ellipse.cpp similarity index 99% rename from test/core/rasterization/ellipse.cpp rename to test/extension/rasterization/ellipse.cpp index 58b5f43458..495e40400a 100644 --- a/test/core/rasterization/ellipse.cpp +++ b/test/extension/rasterization/ellipse.cpp @@ -6,7 +6,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // #include -#include +#include "boost/gil.hpp" #include #include #include diff --git a/test/core/rasterization/line.cpp b/test/extension/rasterization/line.cpp similarity index 98% rename from test/core/rasterization/line.cpp rename to test/extension/rasterization/line.cpp index 42e3bf7469..92986edbf4 100644 --- a/test/core/rasterization/line.cpp +++ b/test/extension/rasterization/line.cpp @@ -9,8 +9,8 @@ #include #include -#include -#include +#include "boost/gil/point.hpp" +#include "boost/gil/extension/rasterization/line.hpp" #include #include From f87ee565c1c4157b880c729d04b1d21d1f6dcc2f Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Wed, 18 May 2022 02:03:36 +0600 Subject: [PATCH 116/193] In depth explanation for Hough line Added in depth explanation for Hough line transform in the associated markdown file --- example/histogram_equalization.md | 46 +++++++++++++++++++++++++++++++ example/hough_transform_line.cpp | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/example/histogram_equalization.md b/example/histogram_equalization.md index c3dcd57832..eb71568dac 100644 --- a/example/histogram_equalization.md +++ b/example/histogram_equalization.md @@ -7,6 +7,52 @@ Histogram equalization capabilities in GIL are demonstrated by the program `hist The program doesn't take any argument on the command line. +Hough line transform solves the equation of a line in reverse, but in *polar coordinates*! The implementation will make each pixel vote on all possible lines it could be part of, limited by input Hough parameters. + +A line in polar coordinates is represented by normal to it in polar coordinates, as shown in example here +https://docs.opencv.org/3.4/d9/db0/tutorial_hough_lines.html + +Since real line is the diagonal throughout the image, the angle of normal to it will 45 degrees. (1s represent real line, 5s represent normal to it). + +``` +1 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 0 0 +0 1 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 0 +0 0 1 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 +0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 0 1 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 0 0 5 1 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 5 0 0 1 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 5 0 0 0 0 1 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 5 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 +0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 +0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 +0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 +0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 +0 0 5 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 1 0 0 +0 5 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 1 0 +5 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 0 1 +``` + +It is obvious that the angle for the expected Hough transform is 45 degrees (angle between 5s and bottom 0s). Now we +need to find the length of 5s. Since 5s, 1s and bottom 0s form a right triangle, we know that bottom 0s are 32 in length +and is a hypotenuse! Using trivial trigonometry we know that the length we are searching for is 32 * cos(45). + `histogram_equalization` expects to find an image called `test_adaptive.png` in the current directory, and produces an image in return, where the equalization have been applied: `histogram_gray_equalized.png`. ## Specific requirements diff --git a/example/hough_transform_line.cpp b/example/hough_transform_line.cpp index 0d9a14f943..d67fff5b59 100644 --- a/example/hough_transform_line.cpp +++ b/example/hough_transform_line.cpp @@ -25,7 +25,7 @@ namespace gil = boost::gil; int main() { - std::ptrdiff_t size = 32; + const std::ptrdiff_t size = 32; gil::gray16_image_t input_image(size, size); auto input_view = gil::view(input_image); From cc39f1a007578c4bfda003641d53cf9c91f6e247 Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Wed, 18 May 2022 02:31:39 +0600 Subject: [PATCH 117/193] Change "" includes into <> --- include/boost/gil/extension/rasterization/circle.hpp | 4 ++-- include/boost/gil/extension/rasterization/line.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/gil/extension/rasterization/circle.hpp b/include/boost/gil/extension/rasterization/circle.hpp index b91650f673..31c3cf6caf 100644 --- a/include/boost/gil/extension/rasterization/circle.hpp +++ b/include/boost/gil/extension/rasterization/circle.hpp @@ -9,8 +9,8 @@ #ifndef BOOST_GIL_RASTERIZATION_CIRCLE_HPP #define BOOST_GIL_RASTERIZATION_CIRCLE_HPP -#include "boost/gil/detail/math.hpp" -#include "boost/gil/point.hpp" +#include +#include #include #include diff --git a/include/boost/gil/extension/rasterization/line.hpp b/include/boost/gil/extension/rasterization/line.hpp index 79ee5029e8..1ff91b6a35 100644 --- a/include/boost/gil/extension/rasterization/line.hpp +++ b/include/boost/gil/extension/rasterization/line.hpp @@ -7,7 +7,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#include "boost/gil/point.hpp" +#include #include #include From 48d7ebffe06bfc97466f6f09e3382cc91aa3a5ed Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Thu, 19 May 2022 16:46:18 +0600 Subject: [PATCH 118/193] Move diffusion and Hough transform Move anisotropic diffusion and Hough transform into extension. Adjust tests and cmakelists --- CMakeLists.txt | 1 + example/anisotropic_diffusion.cpp | 2 +- include/boost/gil.hpp | 6 ++--- .../image_processing/diffusion.hpp | 2 +- .../image_processing/hough_parameter.hpp | 2 +- .../image_processing/hough_transform.hpp | 2 +- test/core/image_processing/CMakeLists.txt | 4 ---- test/extension/CMakeLists.txt | 4 ++++ .../extension/image_processing/CMakeLists.txt | 22 +++++++++++++++++++ .../anisotropic_diffusion.cpp | 6 ++--- .../hough_circle_transform.cpp | 2 +- .../image_processing/hough_line_transform.cpp | 4 ++-- .../image_processing/hough_parameter.cpp | 2 +- 13 files changed, 41 insertions(+), 18 deletions(-) rename include/boost/gil/{ => extension}/image_processing/diffusion.hpp (99%) rename include/boost/gil/{ => extension}/image_processing/hough_parameter.hpp (99%) rename include/boost/gil/{ => extension}/image_processing/hough_transform.hpp (98%) create mode 100644 test/extension/image_processing/CMakeLists.txt rename test/{core => extension}/image_processing/anisotropic_diffusion.cpp (98%) rename test/{core => extension}/image_processing/hough_circle_transform.cpp (97%) rename test/{core => extension}/image_processing/hough_line_transform.cpp (96%) rename test/{core => extension}/image_processing/hough_parameter.cpp (97%) diff --git a/CMakeLists.txt b/CMakeLists.txt index a492c35066..aee245bbfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ option(BOOST_GIL_ENABLE_EXT_IO "Enable IO extension, tests and examples (require option(BOOST_GIL_ENABLE_EXT_NUMERIC "Enable Numeric extension, tests and examples" ON) option(BOOST_GIL_ENABLE_EXT_TOOLBOX "Enable Toolbox extension, tests and examples" ON) option(BOOST_GIL_ENABLE_EXT_RASTERIZATION "Enable Rasterization extension and tests" ON) +option(BOOST_GIL_ENABLE_EXT_IMAGE_PROCESSING "Enable Image Processing extension (!) and tests" ON) option(BOOST_GIL_USE_CONAN "Use Conan to install dependencies" OFF) option(BOOST_GIL_USE_CLANG_TIDY "Set CMAKE_CXX_CLANG_TIDY property on targets to enable clang-tidy linting" OFF) set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard version to use (default is 11)") diff --git a/example/anisotropic_diffusion.cpp b/example/anisotropic_diffusion.cpp index c96533647c..04c95406bd 100644 --- a/example/anisotropic_diffusion.cpp +++ b/example/anisotropic_diffusion.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include "boost/gil/extension/image_processing/diffusion.hpp" #include #include #include diff --git a/include/boost/gil.hpp b/include/boost/gil.hpp index 16088a0978..9bfba80240 100644 --- a/include/boost/gil.hpp +++ b/include/boost/gil.hpp @@ -49,14 +49,14 @@ #include #include #include -#include +#include "boost/gil/extension/image_processing/diffusion.hpp" #include #include #include #include #include -#include -#include +#include "boost/gil/extension/image_processing/hough_parameter.hpp" +#include "boost/gil/extension/image_processing/hough_transform.hpp" #include #include #include diff --git a/include/boost/gil/image_processing/diffusion.hpp b/include/boost/gil/extension/image_processing/diffusion.hpp similarity index 99% rename from include/boost/gil/image_processing/diffusion.hpp rename to include/boost/gil/extension/image_processing/diffusion.hpp index fb1c86a4d3..d40c875c64 100644 --- a/include/boost/gil/image_processing/diffusion.hpp +++ b/include/boost/gil/extension/image_processing/diffusion.hpp @@ -10,7 +10,7 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_DIFFUSION_HPP #define BOOST_GIL_IMAGE_PROCESSING_DIFFUSION_HPP -#include "boost/gil/detail/math.hpp" +#include #include #include #include diff --git a/include/boost/gil/image_processing/hough_parameter.hpp b/include/boost/gil/extension/image_processing/hough_parameter.hpp similarity index 99% rename from include/boost/gil/image_processing/hough_parameter.hpp rename to include/boost/gil/extension/image_processing/hough_parameter.hpp index 97fcd8cde5..66b031346a 100644 --- a/include/boost/gil/image_processing/hough_parameter.hpp +++ b/include/boost/gil/extension/image_processing/hough_parameter.hpp @@ -9,7 +9,7 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_HOUGH_PARAMETER_HPP #define BOOST_GIL_IMAGE_PROCESSING_HOUGH_PARAMETER_HPP -#include +#include "boost/gil/point.hpp" #include #include diff --git a/include/boost/gil/image_processing/hough_transform.hpp b/include/boost/gil/extension/image_processing/hough_transform.hpp similarity index 98% rename from include/boost/gil/image_processing/hough_transform.hpp rename to include/boost/gil/extension/image_processing/hough_transform.hpp index 1de81f0768..39eb8dc1da 100644 --- a/include/boost/gil/image_processing/hough_transform.hpp +++ b/include/boost/gil/extension/image_processing/hough_transform.hpp @@ -10,7 +10,7 @@ #define BOOST_GIL_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP #include -#include +#include #include #include #include diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index 680c41047c..a2a8e08fbc 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -19,10 +19,6 @@ foreach(_name box_filter median_filter sobel_scharr - anisotropic_diffusion - hough_parameter - hough_line_transform - hough_circle_transform convolve convolve_2d convolve_cols diff --git a/test/extension/CMakeLists.txt b/test/extension/CMakeLists.txt index 2d4c9c578a..df1cfd72b3 100644 --- a/test/extension/CMakeLists.txt +++ b/test/extension/CMakeLists.txt @@ -9,6 +9,10 @@ if(BOOST_GIL_ENABLE_EXT_RASTERIZATION) add_subdirectory(rasterization) endif() +if(BOOST_GIL_ENABLE_EXT_IMAGE_PROCESSING) + add_subdirectory(image_processing) +endif() + if(BOOST_GIL_ENABLE_EXT_DYNAMIC_IMAGE) add_subdirectory(dynamic_image) endif() diff --git a/test/extension/image_processing/CMakeLists.txt b/test/extension/image_processing/CMakeLists.txt new file mode 100644 index 0000000000..62e6f79595 --- /dev/null +++ b/test/extension/image_processing/CMakeLists.txt @@ -0,0 +1,22 @@ +foreach(_name + anisotropic_diffusion + hough_circle_transform + hough_line_transform + hough_parameter) + set(_test t_ext_image_processing_${_name}) + set(_target test_ext_image_processing_${_name}) + + add_executable(${_target} "") + target_sources(${_target} PRIVATE ${_name}.cpp) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK) + add_test(NAME ${_test} COMMAND ${_target}) + + unset(_name) + unset(_target) + unset(_test) +endforeach() diff --git a/test/core/image_processing/anisotropic_diffusion.cpp b/test/extension/image_processing/anisotropic_diffusion.cpp similarity index 98% rename from test/core/image_processing/anisotropic_diffusion.cpp rename to test/extension/image_processing/anisotropic_diffusion.cpp index 58a5c9d6ce..302b43de58 100644 --- a/test/core/image_processing/anisotropic_diffusion.cpp +++ b/test/extension/image_processing/anisotropic_diffusion.cpp @@ -5,11 +5,11 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // -#include "../test_fixture.hpp" -#include "boost/gil/algorithm.hpp" +#include +#include #include #include -#include +#include #include #include #include diff --git a/test/core/image_processing/hough_circle_transform.cpp b/test/extension/image_processing/hough_circle_transform.cpp similarity index 97% rename from test/core/image_processing/hough_circle_transform.cpp rename to test/extension/image_processing/hough_circle_transform.cpp index 9b0a3563da..67156e51a1 100644 --- a/test/core/image_processing/hough_circle_transform.cpp +++ b/test/extension/image_processing/hough_circle_transform.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/test/core/image_processing/hough_line_transform.cpp b/test/extension/image_processing/hough_line_transform.cpp similarity index 96% rename from test/core/image_processing/hough_line_transform.cpp rename to test/extension/image_processing/hough_line_transform.cpp index f0ed992fa3..3be4194261 100644 --- a/test/core/image_processing/hough_line_transform.cpp +++ b/test/extension/image_processing/hough_line_transform.cpp @@ -10,10 +10,10 @@ #include #include #include -#include +#include #include #include -#include +#include #include #include #include diff --git a/test/core/image_processing/hough_parameter.cpp b/test/extension/image_processing/hough_parameter.cpp similarity index 97% rename from test/core/image_processing/hough_parameter.cpp rename to test/extension/image_processing/hough_parameter.cpp index 8d1c7b4452..9fd4b4a95f 100644 --- a/test/core/image_processing/hough_parameter.cpp +++ b/test/extension/image_processing/hough_parameter.cpp @@ -7,7 +7,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // #include -#include +#include namespace gil = boost::gil; From b962a6a2dba6a75cab9177fa7a36a66016c9b3cb Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Fri, 27 May 2022 01:40:15 +0600 Subject: [PATCH 119/193] Edit Jamfile to new layout --- test/core/Jamfile | 1 - test/extension/Jamfile | 2 ++ test/extension/image_processing/Jamfile | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/extension/image_processing/Jamfile diff --git a/test/core/Jamfile b/test/core/Jamfile index bc1e5f55d7..9305b8762c 100644 --- a/test/core/Jamfile +++ b/test/core/Jamfile @@ -33,4 +33,3 @@ build-project image_view ; build-project algorithm ; build-project image_processing ; build-project histogram ; -build-project rasterization ; diff --git a/test/extension/Jamfile b/test/extension/Jamfile index 5754513279..8c7790d2e2 100644 --- a/test/extension/Jamfile +++ b/test/extension/Jamfile @@ -11,3 +11,5 @@ build-project histogram ; build-project numeric ; build-project toolbox ; build-project io ; +build-project image_processing ; +build-project rasterization ; diff --git a/test/extension/image_processing/Jamfile b/test/extension/image_processing/Jamfile new file mode 100644 index 0000000000..6ab73640d1 --- /dev/null +++ b/test/extension/image_processing/Jamfile @@ -0,0 +1,14 @@ +# Boost.GIL (Generic Image Library) - tests +# +# Copyright 2020 Olzhas Zhumabek +# +# 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) +# +import testing ; + +run anisotropic_diffusion.cpp ; +run hough_circle_transform.cpp ; +run hough_line_transform.cpp ; +run hough_parameter.cpp ; \ No newline at end of file From 76dcca89f4777f50089a1bb110f8f35cd3fd8318 Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Fri, 27 May 2022 01:53:01 +0600 Subject: [PATCH 120/193] Add compile statements to Jamfile --- test/extension/image_processing/Jamfile | 5 +++++ test/extension/rasterization/Jamfile | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/test/extension/image_processing/Jamfile b/test/extension/image_processing/Jamfile index 6ab73640d1..7320eb40d0 100644 --- a/test/extension/image_processing/Jamfile +++ b/test/extension/image_processing/Jamfile @@ -8,6 +8,11 @@ # import testing ; +compile anisotropic_diffusion.cpp ; +compile hough_circle_transform.cpp ; +compile hough_line_transform.cpp ; +compile hough_parameter.cpp ; + run anisotropic_diffusion.cpp ; run hough_circle_transform.cpp ; run hough_line_transform.cpp ; diff --git a/test/extension/rasterization/Jamfile b/test/extension/rasterization/Jamfile index 6be4cd9705..0a2cae3040 100644 --- a/test/extension/rasterization/Jamfile +++ b/test/extension/rasterization/Jamfile @@ -8,6 +8,10 @@ # import testing ; +compile line.cpp ; +compile circle.cpp ; +compile ellipse.cpp ; + run line.cpp ; run circle.cpp ; run ellipse.cpp ; From 3965f2ab99a27498cb94f761c186fd08cae780ae Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Tue, 31 May 2022 02:37:28 +0600 Subject: [PATCH 121/193] Remove migrated files --- test/core/image_processing/Jamfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/core/image_processing/Jamfile b/test/core/image_processing/Jamfile index 6f4d77f76c..562860e811 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -21,9 +21,6 @@ run hessian.cpp ; run sobel_scharr.cpp ; run box_filter.cpp ; run median_filter.cpp ; -run anisotropic_diffusion.cpp ; -run hough_line_transform.cpp ; -run hough_circle_transform.cpp ; run morphology.cpp ; run convolve.cpp ; run convolve_2d.cpp ; From 45da544873eb32eff2ef4a2c72c641775f11fa43 Mon Sep 17 00:00:00 2001 From: Olzhas Zhumabek Date: Tue, 31 May 2022 02:39:13 +0600 Subject: [PATCH 122/193] Removed redundant lines --- test/extension/image_processing/Jamfile | 5 ----- test/extension/rasterization/Jamfile | 4 ---- 2 files changed, 9 deletions(-) diff --git a/test/extension/image_processing/Jamfile b/test/extension/image_processing/Jamfile index 7320eb40d0..6ab73640d1 100644 --- a/test/extension/image_processing/Jamfile +++ b/test/extension/image_processing/Jamfile @@ -8,11 +8,6 @@ # import testing ; -compile anisotropic_diffusion.cpp ; -compile hough_circle_transform.cpp ; -compile hough_line_transform.cpp ; -compile hough_parameter.cpp ; - run anisotropic_diffusion.cpp ; run hough_circle_transform.cpp ; run hough_line_transform.cpp ; diff --git a/test/extension/rasterization/Jamfile b/test/extension/rasterization/Jamfile index 0a2cae3040..6be4cd9705 100644 --- a/test/extension/rasterization/Jamfile +++ b/test/extension/rasterization/Jamfile @@ -8,10 +8,6 @@ # import testing ; -compile line.cpp ; -compile circle.cpp ; -compile ellipse.cpp ; - run line.cpp ; run circle.cpp ; run ellipse.cpp ; From 2f7c3db5bd8df8fb3dc203c75f5148d7d93cdda0 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Sun, 5 Jun 2022 11:31:34 +0200 Subject: [PATCH 123/193] chore: update metadata for switch to C++ 14 (#682) The switch was already announced in PR 677, but the metadata was not updated at the same time. --- meta/libraries.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta/libraries.json b/meta/libraries.json index e72c3558d8..e87a2fa238 100644 --- a/meta/libraries.json +++ b/meta/libraries.json @@ -6,7 +6,7 @@ "Hailin Jin", "Christian Henning" ], - "description": "(C++11) Generic Image Library", + "description": "(C++14) Generic Image Library", "category": [ "Algorithms", "Containers", @@ -19,5 +19,5 @@ "Mateusz Loskot ", "Pranam Lashkari " ], - "cxxstd": "11" + "cxxstd": "14" } From 9ecdb876b33e06725b3b81e9db3ad0d9f60c58f7 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Tue, 7 Jun 2022 09:24:30 +0200 Subject: [PATCH 124/193] Fix deprecation warning bug for gcc 10.2 (#683) Related to #660, see https://github.com/boostorg/gil/pull/660#issuecomment-1147735559 --- .../gil/extension/dynamic_image/image_view_factory.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp index 1418f162f1..7afb6b12ec 100644 --- a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp +++ b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp @@ -381,11 +381,10 @@ auto color_converted_view(any_image_view const& src) template [[deprecated("Use color_converted_view(const any_image_view& src, CC) instead.")]] inline -auto any_color_converted_view(const any_image_view& src, CC) +auto any_color_converted_view(const any_image_view& src, CC cc) -> typename color_converted_view_type, DstP, CC>::type { - using cc_view_t = typename color_converted_view_type, DstP, CC>::type; - return apply_operation(src, detail::color_converted_view_fn()); + return color_converted_view(src, cc); } /// \ingroup ImageViewTransformationsColorConvert @@ -398,8 +397,7 @@ inline auto any_color_converted_view(const any_image_view& src) -> typename color_converted_view_type, DstP>::type { - using cc_view_t = typename color_converted_view_type, DstP>::type; - return apply_operation(src, detail::color_converted_view_fn()); + return color_converted_view(src); } /// \} From 1c6c427f5394b06296aec62442e361332a9b34d9 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sun, 12 Jun 2022 15:05:31 +0200 Subject: [PATCH 125/193] fix: Added missing include guard to line rasterizer (#687) --- .../gil/extension/image_processing/diffusion.hpp | 5 +++-- .../extension/image_processing/hough_parameter.hpp | 5 +++-- .../extension/image_processing/hough_transform.hpp | 7 ++++--- include/boost/gil/extension/rasterization/circle.hpp | 9 ++++++--- .../boost/gil/extension/rasterization/ellipse.hpp | 8 +++++--- include/boost/gil/extension/rasterization/line.hpp | 12 +++++++----- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/include/boost/gil/extension/image_processing/diffusion.hpp b/include/boost/gil/extension/image_processing/diffusion.hpp index d40c875c64..b8333c6291 100644 --- a/include/boost/gil/extension/image_processing/diffusion.hpp +++ b/include/boost/gil/extension/image_processing/diffusion.hpp @@ -7,8 +7,8 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_GIL_IMAGE_PROCESSING_DIFFUSION_HPP -#define BOOST_GIL_IMAGE_PROCESSING_DIFFUSION_HPP +#ifndef BOOST_GIL_EXTENSION_IMAGE_PROCESSING_DIFFUSION_HPP +#define BOOST_GIL_EXTENSION_IMAGE_PROCESSING_DIFFUSION_HPP #include #include @@ -19,6 +19,7 @@ #include #include #include + #include #include #include diff --git a/include/boost/gil/extension/image_processing/hough_parameter.hpp b/include/boost/gil/extension/image_processing/hough_parameter.hpp index 66b031346a..b21585d3cf 100644 --- a/include/boost/gil/extension/image_processing/hough_parameter.hpp +++ b/include/boost/gil/extension/image_processing/hough_parameter.hpp @@ -6,10 +6,11 @@ // 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_HOUGH_PARAMETER_HPP -#define BOOST_GIL_IMAGE_PROCESSING_HOUGH_PARAMETER_HPP +#ifndef BOOST_GIL_EXTENSION_IMAGE_PROCESSING_HOUGH_PARAMETER_HPP +#define BOOST_GIL_EXTENSION_IMAGE_PROCESSING_HOUGH_PARAMETER_HPP #include "boost/gil/point.hpp" + #include #include diff --git a/include/boost/gil/extension/image_processing/hough_transform.hpp b/include/boost/gil/extension/image_processing/hough_transform.hpp index 39eb8dc1da..f0dada5fea 100644 --- a/include/boost/gil/extension/image_processing/hough_transform.hpp +++ b/include/boost/gil/extension/image_processing/hough_transform.hpp @@ -6,12 +6,13 @@ // 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_HOUGH_TRANSFORM_HPP -#define BOOST_GIL_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP +#ifndef BOOST_GIL_EXTENSION_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP +#define BOOST_GIL_EXTENSION_IMAGE_PROCESSING_HOUGH_TRANSFORM_HPP -#include #include #include + +#include #include #include #include diff --git a/include/boost/gil/extension/rasterization/circle.hpp b/include/boost/gil/extension/rasterization/circle.hpp index 31c3cf6caf..52629741fa 100644 --- a/include/boost/gil/extension/rasterization/circle.hpp +++ b/include/boost/gil/extension/rasterization/circle.hpp @@ -1,4 +1,3 @@ -// Boost.GIL (Generic Image Library) - tests // // Copyright 2020 Olzhas Zhumabek // @@ -6,15 +5,17 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_GIL_RASTERIZATION_CIRCLE_HPP -#define BOOST_GIL_RASTERIZATION_CIRCLE_HPP +#ifndef BOOST_GIL_EXTENSION_RASTERIZATION_CIRCLE_HPP +#define BOOST_GIL_EXTENSION_RASTERIZATION_CIRCLE_HPP #include #include + #include #include namespace boost { namespace gil { + /// \defgroup CircleRasterization /// \ingroup Rasterization /// \brief Circle rasterization algorithms @@ -122,5 +123,7 @@ struct midpoint_circle_rasterizer } } }; + }} // namespace boost::gil + #endif diff --git a/include/boost/gil/extension/rasterization/ellipse.hpp b/include/boost/gil/extension/rasterization/ellipse.hpp index 6ba1dafeb0..6ea6ea46cb 100644 --- a/include/boost/gil/extension/rasterization/ellipse.hpp +++ b/include/boost/gil/extension/rasterization/ellipse.hpp @@ -5,8 +5,8 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_GIL_RASTERIZATION_ELLIPSE_HPP -#define BOOST_GIL_RASTERIZATION_ELLIPSE_HPP +#ifndef BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP +#define BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP #include #include @@ -187,6 +187,8 @@ struct midpoint_elliptical_rasterizer obtain_trajectory(semi_axes); draw_curve(view, colour, center, trajectory_points); } -}; // midpoint elliptical rasterizer +}; + }} // namespace boost::gil + #endif diff --git a/include/boost/gil/extension/rasterization/line.hpp b/include/boost/gil/extension/rasterization/line.hpp index 1ff91b6a35..53dd842540 100644 --- a/include/boost/gil/extension/rasterization/line.hpp +++ b/include/boost/gil/extension/rasterization/line.hpp @@ -1,4 +1,3 @@ -// Boost.GIL (Generic Image Library) - tests // // Copyright 2020 Olzhas Zhumabek // @@ -6,15 +5,16 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // +#ifndef BOOST_GIL_EXTENSION_RASTERIZATION_LINE_HPP +#define BOOST_GIL_EXTENSION_RASTERIZATION_LINE_HPP #include + #include #include -namespace boost -{ -namespace gil -{ +namespace boost { namespace gil { + /// \defgroup Rasterization /// \brief A set of functions to rasterize shapes /// @@ -95,3 +95,5 @@ struct bresenham_line_rasterizer }; }} // namespace boost::gil + +#endif From 5273678bfc61bf6fae0707f6c90001757bfba37d Mon Sep 17 00:00:00 2001 From: Eugene K <34233075+eugene-kulak@users.noreply.github.com> Date: Fri, 24 Jun 2022 02:44:28 -0400 Subject: [PATCH 126/193] fix: Wrong RGB -> HSL convertion (#505) Tests will follow as part of https://github.com/boostorg/gil/issues/690 --- include/boost/gil/extension/toolbox/color_spaces/hsl.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp b/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp index 61e9b50052..c07ba19b8a 100644 --- a/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp +++ b/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp @@ -75,6 +75,7 @@ struct default_color_converter_impl< rgb_t, hsl_t > { float32_t diff = max_color - min_color; + float32_t sum = max_color + min_color; // lightness calculation @@ -84,13 +85,11 @@ struct default_color_converter_impl< rgb_t, hsl_t > if( lightness < 0.5f ) { - saturation = diff - / ( min_color + max_color ); + saturation = diff / ( sum ); } else { - saturation = ( max_color - min_color ) - / ( 2.f - diff ); + saturation = diff / ( 2.f - sum ); } @@ -115,7 +114,7 @@ struct default_color_converter_impl< rgb_t, hsl_t > { // max_color is blue hue = 4.f - + ( temp_red - temp_blue ) + + ( temp_red - temp_green ) / diff; } From c436ea3e6739964e906c3e794ffbaae48978ad38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Fri, 24 Jun 2022 17:21:52 +0200 Subject: [PATCH 127/193] fix: Add missing #include --- include/boost/gil/histogram.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/gil/histogram.hpp b/include/boost/gil/histogram.hpp index fbf3020255..8d78caff1c 100644 --- a/include/boost/gil/histogram.hpp +++ b/include/boost/gil/histogram.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include From 46731e6d449ff1f4acc9735384d9313203dd7f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 25 Jun 2022 13:50:43 +0200 Subject: [PATCH 128/193] chore: Correct include guard --- test/test_utility_output_stream.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_utility_output_stream.hpp b/test/test_utility_output_stream.hpp index ec6aff901c..44585332a6 100644 --- a/test/test_utility_output_stream.hpp +++ b/test/test_utility_output_stream.hpp @@ -5,8 +5,8 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#ifndef BOOST_GIL_TEST_TEST_UTILITY_HPP -#define BOOST_GIL_TEST_TEST_UTILITY_HPP +#ifndef BOOST_GIL_TEST_TEST_UTILITY_OUTPUT_STREAM_HPP +#define BOOST_GIL_TEST_TEST_UTILITY_OUTPUT_STREAM_HPP #include // static_for_each #include From d50d85613add1a52eed45ae51778d6649137865c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 25 Jun 2022 13:52:54 +0200 Subject: [PATCH 129/193] refactor: Make with_tolerance reusable across other tests Refinement of PR #527 --- test/extension/numeric/matrix3x2.cpp | 30 ++++++-------------- test/test_utility_with_tolerance.hpp | 41 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 test/test_utility_with_tolerance.hpp diff --git a/test/extension/numeric/matrix3x2.cpp b/test/extension/numeric/matrix3x2.cpp index 1f0ae8f67d..afc4048cc6 100644 --- a/test/extension/numeric/matrix3x2.cpp +++ b/test/extension/numeric/matrix3x2.cpp @@ -12,24 +12,12 @@ #include +#include "test_utility_with_tolerance.hpp" + #include namespace gil = boost::gil; -// Tolerance predicate for floating point comparison to use with BOOST_TEST_WITH -template -struct with_tolerance -{ - with_tolerance(T tolerance) : tolerance(tolerance) {} - bool operator()(T lhs, T rhs) - { - return (std::abs(lhs - rhs) <= tolerance); - } - -private: - T tolerance; -}; - namespace { constexpr double HALF_PI = 1.57079632679489661923; } @@ -134,10 +122,10 @@ void test_matrix3x2_vector_multiplication() void test_matrix3x2_get_rotate() { auto m1 = gil::matrix3x2::get_rotate(HALF_PI); - BOOST_TEST_WITH(m1.a, std::cos(HALF_PI), with_tolerance(0.03)); + BOOST_TEST_WITH(m1.a, std::cos(HALF_PI), gil::test::utility::with_tolerance(0.03)); BOOST_TEST_EQ(m1.b, 1); BOOST_TEST_EQ(m1.c, -1); - BOOST_TEST_WITH(m1.d, std::cos(HALF_PI), with_tolerance(0.03)); + BOOST_TEST_WITH(m1.d, std::cos(HALF_PI), gil::test::utility::with_tolerance(0.03)); BOOST_TEST_EQ(m1.e, 0); BOOST_TEST_EQ(m1.f, 0); } @@ -197,8 +185,8 @@ void test_matrix3x2_inverse() point_t q = gil::transform(inverse(m), p); point_t p2 = gil::transform(m, q); - BOOST_TEST_WITH(p.x, p2.x, with_tolerance(1e-9)); - BOOST_TEST_WITH(p.y, p2.y, with_tolerance(1e-9)); + BOOST_TEST_WITH(p.x, p2.x, gil::test::utility::with_tolerance(1e-9)); + BOOST_TEST_WITH(p.y, p2.y, gil::test::utility::with_tolerance(1e-9)); } void test_matrix3x2_center_rotate() @@ -208,12 +196,12 @@ void test_matrix3x2_center_rotate() m1 = gil::center_rotate(dimension, HALF_PI); - BOOST_TEST_WITH(m1.a , std::cos(HALF_PI) , with_tolerance(1e-9)); + BOOST_TEST_WITH(m1.a , std::cos(HALF_PI) , gil::test::utility::with_tolerance(1e-9)); BOOST_TEST_EQ (m1.b , 1); BOOST_TEST_EQ (m1.c , -1); - BOOST_TEST_WITH(m1.d , std::cos(HALF_PI) , with_tolerance(1e-9)); + BOOST_TEST_WITH(m1.d , std::cos(HALF_PI) , gil::test::utility::with_tolerance(1e-9)); BOOST_TEST_EQ (m1.e , 100); - BOOST_TEST_WITH(m1.f , std::cos(HALF_PI) , with_tolerance(1e-9)); + BOOST_TEST_WITH(m1.f , std::cos(HALF_PI) , gil::test::utility::with_tolerance(1e-9)); } int main() diff --git a/test/test_utility_with_tolerance.hpp b/test/test_utility_with_tolerance.hpp new file mode 100644 index 0000000000..af5c7816c5 --- /dev/null +++ b/test/test_utility_with_tolerance.hpp @@ -0,0 +1,41 @@ +// +// Copyright 2020 Samuel Debionne +// +// Distributed under 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_TEST_TEST_UTILITY_WITH_TOLERANCE_HPP +#define BOOST_GIL_TEST_TEST_UTILITY_WITH_TOLERANCE_HPP + +#include +#include +#include + +namespace boost { namespace gil { + +namespace test { namespace utility { + +// Tolerance predicate for floating point comparison to use with BOOST_TEST_WITH. +// See https://github.com/boostorg/core/pull/77 for details. +template +struct with_tolerance +{ + with_tolerance(T tolerance) : tolerance(tolerance) + { + } + + bool operator()(T lhs, T rhs) + { + return (std::abs(lhs - rhs) <= tolerance); + } + +private: + T tolerance; +}; + +}} // namespace test::utility + +}} // namespace boost::gil + +#endif From 57c616d2734b1455f9784a7b12a73412e24e2299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 25 Jun 2022 14:53:26 +0200 Subject: [PATCH 130/193] refactor: Move RGB to HSL tests to color_convert_rgb.cpp --- test/extension/toolbox/Jamfile | 1 + test/extension/toolbox/color_convert_hsl.cpp | 34 +----------- test/extension/toolbox/color_convert_rgb.cpp | 56 ++++++++++++++++++++ 3 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 test/extension/toolbox/color_convert_rgb.cpp diff --git a/test/extension/toolbox/Jamfile b/test/extension/toolbox/Jamfile index a2a78f7bed..a2d4980c85 100644 --- a/test/extension/toolbox/Jamfile +++ b/test/extension/toolbox/Jamfile @@ -25,6 +25,7 @@ run color_convert_hsl.cpp ; run color_convert_hsv.cpp ; run color_convert_lab.cpp ; run color_convert_luminance.cpp ; +run color_convert_rgb.cpp ; run color_convert_xyz.cpp ; run indexed_image.cpp ; diff --git a/test/extension/toolbox/color_convert_hsl.cpp b/test/extension/toolbox/color_convert_hsl.cpp index 33c5c19629..a47581fe84 100644 --- a/test/extension/toolbox/color_convert_hsl.cpp +++ b/test/extension/toolbox/color_convert_hsl.cpp @@ -17,18 +17,7 @@ namespace gil = boost::gil; -void test_rgb_to_hsl() -{ - gil::rgb8_pixel_t p{128, 0, 128}; - gil::hsl32f_pixel_t h; - gil::color_convert(p, h); - - BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::hue_t()), 0.8); // 0.83333331 - BOOST_TEST_EQ(gil::get_color(h, gil::hsl_color_space::saturation_t()), 1.0); // 1.00000000 - BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::lightness_t()), 0.25); // 0.25098040 -} - -void test_hsl_to_rgb() +void test_basic_hsl_to_rgb() { gil::rgb8_pixel_t p(64, 0, 64); gil::hsl32f_pixel_t h; @@ -59,29 +48,10 @@ void test_image_assign_hsl() } } -void test_copy_pixels_rgb_to_hsl() -{ - gil::rgb8_image_t rgb_img(320, 240); - gil::rgb8_pixel_t rgb_pix(64, 32, 64); - gil::fill_pixels(view(rgb_img), rgb_pix); - gil::hsl32f_image_t hsl_img(view(rgb_img).dimensions()); - gil::copy_pixels(gil::color_converted_view(view(rgb_img)), view(hsl_img)); - - auto view = gil::view(hsl_img); - for (auto it = view.begin(), end = view.end(); it != end; ++it) - { - gil::rgb8_pixel_t p; - gil::color_convert(*it, p); - BOOST_TEST_EQ(p, rgb_pix); - } -} - int main() { - test_rgb_to_hsl(); - test_hsl_to_rgb(); + test_basic_hsl_to_rgb(); test_image_assign_hsl(); - test_copy_pixels_rgb_to_hsl(); return ::boost::report_errors(); } diff --git a/test/extension/toolbox/color_convert_rgb.cpp b/test/extension/toolbox/color_convert_rgb.cpp new file mode 100644 index 0000000000..f62429bdec --- /dev/null +++ b/test/extension/toolbox/color_convert_rgb.cpp @@ -0,0 +1,56 @@ +// +// Copyright 2013 Christian Henning +// Copyright 2020 Mateusz Loskot +// +// Distributed under 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 + +#include "test_utility_with_tolerance.hpp" + +namespace gil = boost::gil; + + +void test_basic_rgb_to_hsl() +{ + gil::rgb8_pixel_t p{128, 0, 128}; + gil::hsl32f_pixel_t h; + gil::color_convert(p, h); + + BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::hue_t()), 0.8); // 0.83333331 + BOOST_TEST_EQ(gil::get_color(h, gil::hsl_color_space::saturation_t()), 1.0); // 1.00000000 + BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::lightness_t()), 0.25); // 0.25098040 +} + +void test_copy_pixels_rgb_to_hsl() +{ + gil::rgb8_image_t rgb_img(320, 240); + gil::rgb8_pixel_t rgb_pix(64, 32, 64); + gil::fill_pixels(view(rgb_img), rgb_pix); + gil::hsl32f_image_t hsl_img(view(rgb_img).dimensions()); + gil::copy_pixels(gil::color_converted_view(view(rgb_img)), view(hsl_img)); + + auto view = gil::view(hsl_img); + for (auto it = view.begin(), end = view.end(); it != end; ++it) + { + gil::rgb8_pixel_t p; + gil::color_convert(*it, p); + BOOST_TEST_EQ(p, rgb_pix); + } +} + +int main() +{ + test_basic_rgb_to_hsl(); + test_copy_pixels_rgb_to_hsl(); + + return ::boost::report_errors(); +} From 0f435906eade0e0d936bc07260da7fe8e278ff21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 25 Jun 2022 17:51:01 +0100 Subject: [PATCH 131/193] test: Add tests for RGB to HSL (#691) Contributor of PR #505 posted GTest-based tests in. This commit ports those tests to Boost.LightweightTest. Closes #690 --- test/extension/toolbox/color_convert_hsl.cpp | 43 +++++++++++++++++- test/extension/toolbox/color_convert_rgb.cpp | 48 +++++++++++++++++++- 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/test/extension/toolbox/color_convert_hsl.cpp b/test/extension/toolbox/color_convert_hsl.cpp index a47581fe84..299691842f 100644 --- a/test/extension/toolbox/color_convert_hsl.cpp +++ b/test/extension/toolbox/color_convert_hsl.cpp @@ -12,8 +12,11 @@ #include #include +#include +#include #include "test_utility_output_stream.hpp" +#include "test_utility_with_tolerance.hpp" namespace gil = boost::gil; @@ -28,6 +31,43 @@ void test_basic_hsl_to_rgb() BOOST_TEST_EQ(b, p); } +void test_colors_hsl_to_rgb() +{ + using color_t = std::tuple; + std::vector colors = { + {0, 0, 0, 0, 0, 0}, // black + {0, 0, 1, 255, 255, 255}, // white + {0, 1, 0.5, 255, 0, 0}, // red + {2 / 6.f, 1, 0.5, 0, 255, 0}, // lime + {4 / 6.f, 1, 0.5, 0, 0, 255}, // blue + {5 / 6.f, 1, 0.25, 128, 0, 128}, // purple + {3 / 6.f, 1, 0.25, 0, 128, 128}, // teal + {4 / 6.f, 1, 0.25, 0, 0, 128}, // navy + }; + + for (auto&& color : colors) + { + float h{0}, s{0}, l{0}; + std::uint8_t r{0}, g{0}, b{0}; + std::tie(h, s, l, r, g, b) = color; + + gil::hsl32f_pixel_t hsl(h, s, l); + gil::rgb8_pixel_t rgb; + gil::color_convert(hsl, rgb); + + float const abs_error = 1; + BOOST_TEST_WITH( + r, gil::get_color(rgb, gil::red_t()), + gil::test::utility::with_tolerance(abs_error)); + BOOST_TEST_WITH( + g, gil::get_color(rgb, gil::green_t()), + gil::test::utility::with_tolerance(abs_error)); + BOOST_TEST_WITH( + b, gil::get_color(rgb, gil::blue_t()), + gil::test::utility::with_tolerance(abs_error)); + } +} + void test_image_assign_hsl() { std::ptrdiff_t const w = 320; @@ -37,7 +77,7 @@ void test_image_assign_hsl() for (std::ptrdiff_t y = 0; y < h; y++) { gil::hsl32f_view_t::x_iterator hsl_x_it = view(hsl_img).row_begin(y); - float v = static_cast(h - y) / h; + float v = static_cast(h - y) / h; for (std::ptrdiff_t x = 0; x < w; x++) { float const hue = (x + 1.f) / w; @@ -51,6 +91,7 @@ void test_image_assign_hsl() int main() { test_basic_hsl_to_rgb(); + test_colors_hsl_to_rgb(); test_image_assign_hsl(); return ::boost::report_errors(); diff --git a/test/extension/toolbox/color_convert_rgb.cpp b/test/extension/toolbox/color_convert_rgb.cpp index f62429bdec..cb22727a1c 100644 --- a/test/extension/toolbox/color_convert_rgb.cpp +++ b/test/extension/toolbox/color_convert_rgb.cpp @@ -13,12 +13,13 @@ #include #include +#include +#include "test_utility_output_stream.hpp" #include "test_utility_with_tolerance.hpp" namespace gil = boost::gil; - void test_basic_rgb_to_hsl() { gil::rgb8_pixel_t p{128, 0, 128}; @@ -30,6 +31,50 @@ void test_basic_rgb_to_hsl() BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::lightness_t()), 0.25); // 0.25098040 } +void test_colors_rgb_to_hsl() +{ + using color_t = std::tuple; + std::vector colors = { + {0, 0, 0, 0, 0, 0}, // black + {255, 255, 255, 0, 0, 1}, // white + {255, 0, 0, 0, 1, 0.5}, // red + {0, 255, 0, 2 / 6.f, 1, 0.5}, // lime + {0, 0, 255, 4 / 6.f, 1, 0.5}, // blue + {255, 255, 0, 1 / 6.f, 1, 0.5}, // yellow + {0, 255, 255, 3 / 6.f, 1, 0.5}, // cyan + {255, 0, 255, 5 / 6.f, 1, 0.5}, // magenta + {191, 191, 191, 0, 0, 0.75}, // silver + {128, 128, 128, 0, 0, 0.5}, // gray + {128, 0, 0, 0, 1, 0.25}, // maroon + {128, 128, 0, 1 / 6.f, 1, 0.25}, // olive + {0, 128, 0, 2 / 6.f, 1, 0.25}, // green + {128, 0, 128, 5 / 6.f, 1, 0.25}, // purple + {0, 128, 128, 3 / 6.f, 1, 0.25}, // teal + {0, 0, 128, 4 / 6.f, 1, 0.25}, // navy + }; + + for (auto&& color : colors) + { + std::uint8_t r{0}, g{0}, b{0}; + float h{0}, s{0}, l{0}; + std::tie(r, g, b, h, s, l) = color; + gil::rgb8_pixel_t rgb(r, g, b); + gil::hsl32f_pixel_t hsl; + gil::color_convert(rgb, hsl); + + float const abs_error = 0.002; + BOOST_TEST_WITH( + h, get_color(hsl, gil::hsl_color_space::hue_t()), + gil::test::utility::with_tolerance(abs_error)); + BOOST_TEST_WITH( + s, get_color(hsl, gil::hsl_color_space::saturation_t()), + gil::test::utility::with_tolerance(abs_error)); + BOOST_TEST_WITH( + l, get_color(hsl, gil::hsl_color_space::lightness_t()), + gil::test::utility::with_tolerance(abs_error)); + } +} + void test_copy_pixels_rgb_to_hsl() { gil::rgb8_image_t rgb_img(320, 240); @@ -50,6 +95,7 @@ void test_copy_pixels_rgb_to_hsl() int main() { test_basic_rgb_to_hsl(); + test_colors_rgb_to_hsl(); test_copy_pixels_rgb_to_hsl(); return ::boost::report_errors(); From 526c8983364a68cb5222fc54c193b1cbbe541a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 25 Jun 2022 22:02:21 +0200 Subject: [PATCH 132/193] test: Add tiled TIFF test case to simple_all_formats This is to hit more lines and make codecov happier, than during recent build of PR #685 --- test/extension/io/simple_all_formats.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/extension/io/simple_all_formats.cpp b/test/extension/io/simple_all_formats.cpp index b4bc87adca..1b110dc143 100644 --- a/test/extension/io/simple_all_formats.cpp +++ b/test/extension/io/simple_all_formats.cpp @@ -74,6 +74,18 @@ void test_tiff() gil::write_view(tiff_out + "simple_all_format.tif", gil::view(img), gil::tiff_tag()); } +void test_tiff_tiled() +{ + gil::rgba8_image_t img; + gil::read_image(tiff_filename, img, gil::tiff_tag()); + + fs::create_directories(fs::path(tiff_out)); + gil::image_write_info info; + info._is_tiled = true; + info._tile_width = info._tile_length = 64; // must be multiples of 16 + gil::write_view(tiff_out + "simple_all_format.tif", gil::view(img), info); +} + int main(int argc, char* argv[]) { try @@ -85,6 +97,7 @@ int main(int argc, char* argv[]) // TODO: test_raw() test_targa(); test_tiff(); + test_tiff_tiled(); } catch (std::exception const& e) { From b9e652dce823e5a117e8e49a884ab71e22570e77 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sat, 25 Jun 2022 23:25:12 +0200 Subject: [PATCH 133/193] fix: Automatic detection of header (#684) --- include/boost/gil/io/detail/filesystem.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/gil/io/detail/filesystem.hpp b/include/boost/gil/io/detail/filesystem.hpp index 2e4c702a75..7ad18cec30 100644 --- a/include/boost/gil/io/detail/filesystem.hpp +++ b/include/boost/gil/io/detail/filesystem.hpp @@ -10,8 +10,8 @@ #include -#if !defined(BOOST_GIL_IO_USE_BOOST_FILESYSTEM) -#if !defined(BOOST_NO_CXX17_HDR_FILESYSTEM) || defined(__cpp_lib_filesystem) +#if !defined(BOOST_GIL_IO_USE_BOOST_FILESYSTEM) && !defined(BOOST_NO_CXX17_HDR_FILESYSTEM) +#if defined(__cpp_lib_filesystem) #include #define BOOST_GIL_IO_USE_STD_FILESYSTEM #elif defined(__cpp_lib_experimental_filesystem) @@ -19,7 +19,7 @@ #define BOOST_GIL_IO_USE_STD_FILESYSTEM #define BOOST_GIL_IO_USE_STD_EXPERIMENTAL_FILESYSTEM #endif -#endif // !BOOST_GIL_IO_USE_BOOST_FILESYSTEM +#endif // !BOOST_GIL_IO_USE_BOOST_FILESYSTEM && !BOOST_NO_CXX17_HDR_FILESYSTEM #if !defined(BOOST_GIL_IO_USE_STD_FILESYSTEM) // Disable warning: conversion to 'std::atomic::__integral_type {aka int}' from 'long int' may alter its value From 151fd9c8a318a4249e244eab959592143422a04d Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sun, 26 Jun 2022 00:51:24 +0200 Subject: [PATCH 134/193] refactor: Replace deprecated libtiff v4.3 typedefs with C99 fixed-size integers (#685) --- .../gil/extension/io/tiff/detail/device.hpp | 37 ++++++++++--------- .../gil/extension/io/tiff/detail/write.hpp | 11 +++--- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/include/boost/gil/extension/io/tiff/detail/device.hpp b/include/boost/gil/extension/io/tiff/detail/device.hpp index 26eb9cdbcf..a765a4ca60 100644 --- a/include/boost/gil/extension/io/tiff/detail/device.hpp +++ b/include/boost/gil/extension/io/tiff/detail/device.hpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -192,7 +193,7 @@ class tiff_device_base { io_error_if( TIFFReadScanline( _tiff_file.get() , reinterpret_cast< tdata_t >( &buffer.front() ) - , (uint32) row + , static_cast( row ) , plane ) == -1 , "Read error." ); @@ -205,7 +206,7 @@ class tiff_device_base { io_error_if( TIFFReadScanline( _tiff_file.get() , reinterpret_cast< tdata_t >( buffer ) - , (uint32) row + , static_cast( row ) , plane ) == -1 , "Read error." ); @@ -221,9 +222,9 @@ class tiff_device_base { if( TIFFReadTile( _tiff_file.get() , reinterpret_cast< tdata_t >( &buffer.front() ) - , (uint32) x - , (uint32) y - , (uint32) z + , static_cast< std::uint32_t >( x ) + , static_cast< std::uint32_t >( y ) + , static_cast< std::uint32_t >( z ) , plane ) == -1 ) { @@ -234,9 +235,9 @@ class tiff_device_base } template< typename Buffer > - void write_scaline( Buffer& buffer - , uint32 row - , tsample_t plane + void write_scaline( Buffer& buffer + , std::uint32_t row + , tsample_t plane ) { io_error_if( TIFFWriteScanline( _tiff_file.get() @@ -248,9 +249,9 @@ class tiff_device_base ); } - void write_scaline( byte_t* buffer - , uint32 row - , tsample_t plane + void write_scaline( byte_t* buffer + , std::uint32_t row + , tsample_t plane ) { io_error_if( TIFFWriteScanline( _tiff_file.get() @@ -263,11 +264,11 @@ class tiff_device_base } template< typename Buffer > - void write_tile( Buffer& buffer - , uint32 x - , uint32 y - , uint32 z - , tsample_t plane + void write_tile( Buffer& buffer + , std::uint32_t x + , std::uint32_t y + , std::uint32_t z + , tsample_t plane ) { if( TIFFWriteTile( _tiff_file.get() @@ -300,8 +301,8 @@ class tiff_device_base ) { bool result = true; - uint32 tw = static_cast< uint32 >( width ); - uint32 th = static_cast< uint32 >( height ); + std::uint32_t tw = static_cast< std::uint32_t >( width ); + std::uint32_t th = static_cast< std::uint32_t >( height ); TIFFDefaultTileSize( _tiff_file.get() , &tw diff --git a/include/boost/gil/extension/io/tiff/detail/write.hpp b/include/boost/gil/extension/io/tiff/detail/write.hpp index cd83a26ff6..9bc7b7c17e 100644 --- a/include/boost/gil/extension/io/tiff/detail/write.hpp +++ b/include/boost/gil/extension/io/tiff/detail/write.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -183,7 +184,7 @@ class writer< Device this->_io_dev.write_scaline( row - , (uint32) y + , static_cast( y ) , 0 ); @@ -211,7 +212,7 @@ class writer< Device this->_io_dev.write_scaline( row - , (uint32) y + , static_cast( y ) , 0 ); @@ -273,7 +274,7 @@ class writer< Device ); this->_io_dev.write_scaline( row_addr - , (uint32) y + , static_cast( y ) , 0 ); @@ -393,8 +394,8 @@ class writer< Device } this->_io_dev.write_tile( row - , static_cast< uint32 >( j ) - , static_cast< uint32 >( i ) + , static_cast< std::uint32_t >( j ) + , static_cast< std::uint32_t >( i ) , 0 , 0 ); From bfed3de0049ed1f0de2f9045ee5b1409666330dc Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sun, 26 Jun 2022 13:21:18 +0200 Subject: [PATCH 135/193] refactor: Deprecate apply_operation in favor of variant2::visit for any_image (#656) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build: Add /bigobj to MSVC flags Avoid test\extension\dynamic_image\algorithm\copy_and_convert_pixels.cpp compilation error: fatal error C1128: number of sections exceeded object file format limit: compile with /bigobj Co-authored-by: Mateusz Łoskot --- RELEASES.md | 8 +- doc/design/dynamic_image.rst | 16 +- doc/tutorial/gradient.rst | 14 +- .../gil/extension/dynamic_image/algorithm.hpp | 52 ++--- .../gil/extension/dynamic_image/any_image.hpp | 11 +- .../dynamic_image/any_image_view.hpp | 8 +- .../dynamic_image/apply_operation.hpp | 2 + .../dynamic_image/image_view_factory.hpp | 39 ++-- .../gil/extension/io/bmp/detail/read.hpp | 4 +- .../gil/extension/io/bmp/detail/write.hpp | 4 +- .../gil/extension/io/jpeg/detail/read.hpp | 4 +- .../gil/extension/io/jpeg/detail/write.hpp | 2 +- .../gil/extension/io/png/detail/read.hpp | 4 +- .../gil/extension/io/png/detail/write.hpp | 2 +- .../gil/extension/io/pnm/detail/read.hpp | 4 +- .../gil/extension/io/pnm/detail/write.hpp | 2 +- .../gil/extension/io/raw/detail/read.hpp | 4 +- .../gil/extension/io/targa/detail/read.hpp | 4 +- .../gil/extension/io/targa/detail/write.hpp | 2 +- .../gil/extension/io/tiff/detail/read.hpp | 4 +- .../gil/extension/io/tiff/detail/write.hpp | 2 +- .../boost/gil/extension/numeric/resample.hpp | 10 +- include/boost/gil/io/detail/dynamic.hpp | 2 +- test/Jamfile | 9 +- test/extension/dynamic_image/Jamfile | 1 + .../extension/dynamic_image/algorithm/Jamfile | 6 +- .../algorithm/copy_and_convert_pixels.cpp | 202 ++++++++++++++++++ .../dynamic_image/algorithm/copy_pixels.cpp | 163 ++++++++++++++ .../dynamic_image/algorithm/equal_pixels.cpp | 104 +++++++++ .../dynamic_image/algorithm/fill_pixels.cpp | 86 ++++++++ .../algorithm/for_each_pixel.cpp | 8 +- test/extension/dynamic_image/any_image.cpp | 26 ++- .../dynamic_image/any_image_view.cpp | 10 +- .../dynamic_image/image_view_factory.cpp | 92 ++++++++ .../extension/dynamic_image/subimage_view.cpp | 9 +- 35 files changed, 804 insertions(+), 116 deletions(-) create mode 100644 test/extension/dynamic_image/algorithm/copy_and_convert_pixels.cpp create mode 100644 test/extension/dynamic_image/algorithm/copy_pixels.cpp create mode 100644 test/extension/dynamic_image/algorithm/equal_pixels.cpp create mode 100644 test/extension/dynamic_image/algorithm/fill_pixels.cpp create mode 100644 test/extension/dynamic_image/image_view_factory.cpp diff --git a/RELEASES.md b/RELEASES.md index fc12f0c8d2..419ca6e145 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -28,6 +28,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). support at least C++14 are considered unsupported as of now. - BREAKING: `any_color_converted_view()` is deprecated and will be removed in the next release. Use `color_converted_view()` instead, which provides the same feature. +- BREAKING: `apply_operation` for `any_image` is deprecated and will be removed in the next release. + Use `variant2::visit` instead, which provides the same feature. - documentation: Display that GIL is a header-only library - Moved numeric extension to core ([PR #573](https://github.com/boostorg/gil/pull/573)) - Added support for C++17's `` ([PR #636](https://github.com/boostorg/gil/pull/636)). @@ -327,10 +329,10 @@ linked PDF documents with detailed changes. ### Changed - Updated the design guide and tutorial, updated syntax of concepts to the latest concepts proposal. -- In `image`, `image_view`, `any_image`, `any_image_view`: +- In `image`, `image_view`, `any_image`, `any_image_view`: There are no longer global functions `get_width()`, `get_height()`, `get_dimensions()`, `num_channels()`. Use methods `width()`, `height()`, `dimensions()` instead. -- In models of pixel, pixel iterator, pixel locator, image view and image: +- In models of pixel, pixel iterator, pixel locator, image view and image: There used to be different ways of getting to a pixel, channel, color space, etc. of an image view, pixel, locator, iterator and image (e.g. traits, member typedefs). Now all pixel-based GIL constructs (pixels, pixel iterators, locators, image views and images) model @@ -338,7 +340,7 @@ linked PDF documents with detailed changes. and for homogeneous constructs we also have: `channel_type`. To get the pixel type or pixel reference/const reference type of an image, image view, locator and pixel, use member typedefs `value_type`, `reference` and `const_reference`. -- In `locator`, `image`, `image_view`, `any_image` and `any_image_view`: +- In `locator`, `image`, `image_view`, `any_image` and `any_image_view`: Removed `dynamic_x_step_t`, `dynamic_y_step_t`, `dynamic_xy_step_t` and `dynamic_xy_step_transposed_t` as member typedefs of locators and image views. Instead, there are separate concepts `HasDynamicXStepTypeConcept`, `HasDynamicYStepTypeConcept`, diff --git a/doc/design/dynamic_image.rst b/doc/design/dynamic_image.rst index f43c5eaffd..546bfa4d9f 100644 --- a/doc/design/dynamic_image.rst +++ b/doc/design/dynamic_image.rst @@ -103,7 +103,7 @@ GIL ``any_image_view`` and ``any_image`` are subclasses of ``variant``: y_coord_t height() const; }; -Operations are invoked on variants via ``apply_operation`` passing a +Operations are invoked on variants via ``variant2::visit`` passing a function object to perform the operation. The code for every allowed type in the variant is instantiated and the appropriate instantiation is selected via a switch statement. Since image view algorithms @@ -129,7 +129,7 @@ pixels. There is no "any_pixel" or "any_pixel_iterator" in GIL. Such constructs could be provided via the ``variant`` mechanism, but doing so would result in inefficient algorithms, since the type resolution would have to be performed per pixel. Image-level algorithms should be -implemented via ``apply_operation``. That said, many common operations +implemented via ``variant2::visit``. That said, many common operations are shared between the static and dynamic types. In addition, all of the image view transformations and many STL-like image view algorithms have overloads operating on ``any_image_view``, as illustrated with @@ -180,14 +180,13 @@ implemented: template typename dynamic_xy_step_type::type rotated180_view(const View& src) { ... } - namespace detail - { + namespace detail { // the function, wrapped inside a function object template struct rotated180_view_fn { typedef Result result_type; template result_type operator()(const View& src) const - { + { return result_type(rotated180_view(src)); } }; @@ -195,10 +194,11 @@ implemented: // overloading of the function using variant. Takes and returns run-time bound view. // The returned view has a dynamic step - template inline // Models MPL Random Access Container of models of ImageViewConcept - typename dynamic_xy_step_type >::type rotated180_view(const any_image_view& src) + template inline + typename dynamic_xy_step_type>::type rotated180_view(const any_image_view& src) { - return apply_operation(src,detail::rotated180_view_fn >::type>()); + using result_view_t = typename dynamic_xy_step_type>::type; + return variant2::visit(detail::rotated180_view_fn(), src); } Variants should be used with caution (especially algorithms that take diff --git a/doc/tutorial/gradient.rst b/doc/tutorial/gradient.rst index bbcb931e19..cc59d22e3e 100644 --- a/doc/tutorial/gradient.rst +++ b/doc/tutorial/gradient.rst @@ -870,22 +870,22 @@ source view: }; The second step is to provide an overload of ``x_luminosity_gradient`` that -takes image view variant and calls GIL's ``apply_operation`` passing it the +takes image view variant and calls ``variant2::visit`` passing it the function object: .. code-block:: cpp - template - void x_luminosity_gradient(const any_image_view& src, const DstView& dst) + template + void x_luminosity_gradient(const any_image_view& src, const DstView& dst) { - apply_operation(src, x_gradient_obj(dst)); + variant2::visit(x_gradient_obj(dst), src); } -``any_image_view`` is the image view variant. It is -templated over ``SrcViews``, an enumeration of all possible view types +``any_image_view`` is the image view variant. It is +templated over ``SrcViews...``, an enumeration of all possible view types the variant can take. ``src`` contains inside an index of the currently instantiated type, as well as a block of memory containing -the instance. ``apply_operation`` goes through a switch statement +the instance. ``variant2::visit`` goes through a switch statement over the index, each case of which casts the memory to the correct view type and invokes the function object with it. Invoking an algorithm on a variant has the overhead of one switch diff --git a/include/boost/gil/extension/dynamic_image/algorithm.hpp b/include/boost/gil/extension/dynamic_image/algorithm.hpp index 75a4aedb7f..174be8f31e 100644 --- a/include/boost/gil/extension/dynamic_image/algorithm.hpp +++ b/include/boost/gil/extension/dynamic_image/algorithm.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2022 Marco Langer // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -12,7 +13,10 @@ #include +#include + #include +#include //////////////////////////////////////////////////////////////////////////////////////// /// \file @@ -43,31 +47,31 @@ struct equal_pixels_fn : binary_operation_obj /// \tparam Types Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam View Model MutableImageViewConcept template -bool equal_pixels(any_image_view const& src, View const& dst) +auto equal_pixels(any_image_view const& src, View const& dst) -> bool { - return apply_operation( - src, - std::bind(detail::equal_pixels_fn(), std::placeholders::_1, dst)); + return variant2::visit( + std::bind(detail::equal_pixels_fn(), std::placeholders::_1, dst), + src); } /// \ingroup ImageViewSTLAlgorithmsEqualPixels /// \tparam View Model ImageViewConcept /// \tparam Types Model Boost.MP11-compatible list of models of MutableImageViewConcept template -bool equal_pixels(View const& src, any_image_view const& dst) +auto equal_pixels(View const& src, any_image_view const& dst) -> bool { - return apply_operation( - dst, - std::bind(detail::equal_pixels_fn(), src, std::placeholders::_1)); + return variant2::visit( + std::bind(detail::equal_pixels_fn(), src, std::placeholders::_1), + dst); } /// \ingroup ImageViewSTLAlgorithmsEqualPixels /// \tparam Types1 Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam Types2 Model Boost.MP11-compatible list of models of MutableImageViewConcept template -bool equal_pixels(any_image_view const& src, any_image_view const& dst) +auto equal_pixels(any_image_view const& src, any_image_view const& dst) -> bool { - return apply_operation(src, dst, detail::equal_pixels_fn()); + return variant2::visit(detail::equal_pixels_fn(), src, dst); } namespace detail { @@ -90,7 +94,7 @@ struct copy_pixels_fn : public binary_operation_obj template void copy_pixels(any_image_view const& src, View const& dst) { - apply_operation(src, std::bind(detail::copy_pixels_fn(), std::placeholders::_1, dst)); + variant2::visit(std::bind(detail::copy_pixels_fn(), std::placeholders::_1, dst), src); } /// \ingroup ImageViewSTLAlgorithmsCopyPixels @@ -99,7 +103,7 @@ void copy_pixels(any_image_view const& src, View const& dst) template void copy_pixels(View const& src, any_image_view const& dst) { - apply_operation(dst, std::bind(detail::copy_pixels_fn(), src, std::placeholders::_1)); + variant2::visit(std::bind(detail::copy_pixels_fn(), src, std::placeholders::_1), dst); } /// \ingroup ImageViewSTLAlgorithmsCopyPixels @@ -108,7 +112,7 @@ void copy_pixels(View const& src, any_image_view const& dst) template void copy_pixels(any_image_view const& src, any_image_view const& dst) { - apply_operation(src, dst, detail::copy_pixels_fn()); + variant2::visit(detail::copy_pixels_fn(), src, dst); } //forward declaration for default_color_converter (see full definition in color_convert.hpp) @@ -122,7 +126,7 @@ template void copy_and_convert_pixels(any_image_view const& src, View const& dst, CC cc) { using cc_fn = detail::copy_and_convert_pixels_fn; - apply_operation(src, std::bind(cc_fn{cc}, std::placeholders::_1, dst)); + variant2::visit(std::bind(cc_fn{cc}, std::placeholders::_1, dst), src); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -132,7 +136,7 @@ template void copy_and_convert_pixels(any_image_view const& src, View const& dst) { using cc_fn = detail::copy_and_convert_pixels_fn; - apply_operation(src, std::bind(cc_fn{}, std::placeholders::_1, dst)); + variant2::visit(std::bind(cc_fn{}, std::placeholders::_1, dst), src); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -143,7 +147,7 @@ template void copy_and_convert_pixels(View const& src, any_image_view const& dst, CC cc) { using cc_fn = detail::copy_and_convert_pixels_fn; - apply_operation(dst, std::bind(cc_fn{cc}, src, std::placeholders::_1)); + variant2::visit(std::bind(cc_fn{cc}, src, std::placeholders::_1), dst); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -153,7 +157,7 @@ template void copy_and_convert_pixels(View const& src, any_image_view const& dst) { using cc_fn = detail::copy_and_convert_pixels_fn; - apply_operation(dst, std::bind(cc_fn{}, src, std::placeholders::_1)); + variant2::visit(std::bind(cc_fn{}, src, std::placeholders::_1), dst); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -165,7 +169,7 @@ void copy_and_convert_pixels( any_image_view const& src, any_image_view const& dst, CC cc) { - apply_operation(src, dst, detail::copy_and_convert_pixels_fn(cc)); + variant2::visit(detail::copy_and_convert_pixels_fn(cc), src, dst); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -176,8 +180,8 @@ void copy_and_convert_pixels( any_image_view const& src, any_image_view const& dst) { - apply_operation(src, dst, - detail::copy_and_convert_pixels_fn()); + variant2::visit( + detail::copy_and_convert_pixels_fn(), src, dst); } namespace detail { @@ -186,7 +190,7 @@ template struct fill_pixels_fn1 { template - static void apply(V const &src, Value const &val) { fill_pixels(src, val); } + static void apply(V const& src, Value const& val) { fill_pixels(src, val); } }; // copy_pixels invoked on incompatible images @@ -194,7 +198,7 @@ template <> struct fill_pixels_fn1 { template - static void apply(V const &, Value const &) { throw std::bad_cast();} + static void apply(V const&, Value const&) { throw std::bad_cast();} }; template @@ -227,7 +231,7 @@ struct fill_pixels_fn template void fill_pixels(any_image_view const& view, Value const& val) { - apply_operation(view, detail::fill_pixels_fn(val)); + variant2::visit(detail::fill_pixels_fn(val), view); } namespace detail { @@ -236,7 +240,7 @@ template struct for_each_pixel_fn { for_each_pixel_fn(F&& fun) : fun_(std::move(fun)) {} - + template auto operator()(View const& view) -> F { diff --git a/include/boost/gil/extension/dynamic_image/any_image.hpp b/include/boost/gil/extension/dynamic_image/any_image.hpp index 457ba4ace9..590a193570 100644 --- a/include/boost/gil/extension/dynamic_image/any_image.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image.hpp @@ -10,7 +10,6 @@ #define BOOST_GIL_EXTENSION_DYNAMIC_IMAGE_ANY_IMAGE_HPP #include -#include #include #include @@ -121,7 +120,7 @@ class any_image : public variant2::variant void recreate(const point_t& dims, unsigned alignment=1) { - apply_operation(*this, detail::recreate_image_fnobj(dims, alignment)); + variant2::visit(detail::recreate_image_fnobj(dims, alignment), *this); } void recreate(x_coord_t width, y_coord_t height, unsigned alignment=1) @@ -131,12 +130,12 @@ class any_image : public variant2::variant std::size_t num_channels() const { - return apply_operation(*this, detail::any_type_get_num_channels()); + return variant2::visit(detail::any_type_get_num_channels(), *this); } point_t dimensions() const { - return apply_operation(*this, detail::any_type_get_dimensions()); + return variant2::visit(detail::any_type_get_dimensions(), *this); } x_coord_t width() const { return dimensions().x; } @@ -156,7 +155,7 @@ BOOST_FORCEINLINE auto view(any_image& img) -> typename any_image::view_t { using view_t = typename any_image::view_t; - return apply_operation(img, detail::any_image_get_view()); + return variant2::visit(detail::any_image_get_view(), img); } /// \brief Returns the constant-pixel view of any image. The returned view is any view. @@ -166,7 +165,7 @@ BOOST_FORCEINLINE auto const_view(any_image const& img) -> typename any_image::const_view_t { using view_t = typename any_image::const_view_t; - return apply_operation(img, detail::any_image_get_const_view()); + return variant2::visit(detail::any_image_get_const_view(), img); } ///@} diff --git a/include/boost/gil/extension/dynamic_image/any_image_view.hpp b/include/boost/gil/extension/dynamic_image/any_image_view.hpp index 2a414e25ad..5b9c94fbe1 100644 --- a/include/boost/gil/extension/dynamic_image/any_image_view.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image_view.hpp @@ -68,7 +68,7 @@ struct any_type_get_size /// Other requirements, such as access to the pixels, would be inefficient to provide. Thus \p any_image_view does not fully model ImageViewConcept. /// However, many algorithms provide overloads taking runtime specified views and thus in many cases \p any_image_view can be used in places taking a view. /// -/// To perform an algorithm on any_image_view, put the algorithm in a function object and invoke it by calling \p apply_operation(runtime_view, algorithm_fn); +/// To perform an algorithm on any_image_view, put the algorithm in a function object and invoke it by calling \p variant2::visit(algorithm_fn, runtime_view); //////////////////////////////////////////////////////////////////////////////////////// template @@ -105,9 +105,9 @@ class any_image_view : public variant2::variant return *this; } - std::size_t num_channels() const { return apply_operation(*this, detail::any_type_get_num_channels()); } - point_t dimensions() const { return apply_operation(*this, detail::any_type_get_dimensions()); } - size_type size() const { return apply_operation(*this, detail::any_type_get_size()); } + std::size_t num_channels() const { return variant2::visit(detail::any_type_get_num_channels(), *this); } + point_t dimensions() const { return variant2::visit(detail::any_type_get_dimensions(), *this); } + size_type size() const { return variant2::visit(detail::any_type_get_size(), *this); } x_coord_t width() const { return dimensions().x; } y_coord_t height() const { return dimensions().y; } }; diff --git a/include/boost/gil/extension/dynamic_image/apply_operation.hpp b/include/boost/gil/extension/dynamic_image/apply_operation.hpp index 2a5b542f5b..48ff1ac3b1 100644 --- a/include/boost/gil/extension/dynamic_image/apply_operation.hpp +++ b/include/boost/gil/extension/dynamic_image/apply_operation.hpp @@ -15,6 +15,7 @@ namespace boost { namespace gil { /// \ingroup Variant /// \brief Applies the visitor op to the variants template +[[deprecated("Use variant2::visit instead.")]] BOOST_FORCEINLINE auto apply_operation(Variant1&& arg1, Visitor&& op) #if defined(BOOST_NO_CXX14_DECLTYPE_AUTO) || defined(BOOST_NO_CXX11_DECLTYPE_N3276) @@ -27,6 +28,7 @@ auto apply_operation(Variant1&& arg1, Visitor&& op) /// \ingroup Variant /// \brief Applies the visitor op to the variants template +[[deprecated("Use variant2::visit instead.")]] BOOST_FORCEINLINE auto apply_operation(Variant1&& arg1, Variant2&& arg2, Visitor&& op) #if defined(BOOST_NO_CXX14_DECLTYPE_AUTO) || defined(BOOST_NO_CXX11_DECLTYPE_N3276) diff --git a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp index 7afb6b12ec..6667a828fa 100644 --- a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp +++ b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp @@ -174,7 +174,7 @@ auto flipped_up_down_view(any_image_view const& src) -> typename dynamic_y_step_type>::type { using result_view_t = typename dynamic_y_step_type>::type; - return apply_operation(src, detail::flipped_up_down_view_fn()); + return variant2::visit(detail::flipped_up_down_view_fn(), src); } /// \ingroup ImageViewTransformationsFlipLR @@ -185,39 +185,40 @@ auto flipped_left_right_view(any_image_view const& src) -> typename dynamic_x_step_type>::type { using result_view_t = typename dynamic_x_step_type>::type; - return apply_operation(src, detail::flipped_left_right_view_fn()); + return variant2::visit(detail::flipped_left_right_view_fn(), src); } /// \ingroup ImageViewTransformationsTransposed /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto transposed_view(const any_image_view& src) +auto transposed_view(any_image_view const& src) -> typename dynamic_xy_step_transposed_type>::type { using result_view_t = typename dynamic_xy_step_transposed_type>::type; - return apply_operation(src, detail::tranposed_view_fn()); + return variant2::visit(detail::tranposed_view_fn(), src); } /// \ingroup ImageViewTransformations90CW /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto rotated90cw_view(const any_image_view& src) +auto rotated90cw_view(any_image_view const& src) -> typename dynamic_xy_step_transposed_type>::type { using result_view_t = typename dynamic_xy_step_transposed_type>::type; - return apply_operation(src,detail::rotated90cw_view_fn()); + return variant2::visit(detail::rotated90cw_view_fn(), src); } /// \ingroup ImageViewTransformations90CCW /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto rotated90ccw_view(const any_image_view& src) +auto rotated90ccw_view(any_image_view const& src) -> typename dynamic_xy_step_transposed_type>::type { - return apply_operation(src,detail::rotated90ccw_view_fn>::type>()); + using result_view_t = typename dynamic_xy_step_transposed_type>::type; + return variant2::visit(detail::rotated90ccw_view_fn(), src); } /// \ingroup ImageViewTransformations180 @@ -227,8 +228,8 @@ inline auto rotated180_view(any_image_view const& src) -> typename dynamic_xy_step_type>::type { - using step_type = typename dynamic_xy_step_type>::type; - return apply_operation(src, detail::rotated180_view_fn()); + using result_view_t = typename dynamic_xy_step_type>::type; + return variant2::visit(detail::rotated180_view_fn(), src); } /// \ingroup ImageViewTransformationsSubimage @@ -242,7 +243,7 @@ auto subimage_view( -> any_image_view { using subimage_view_fn = detail::subimage_view_fn>; - return apply_operation(src, subimage_view_fn(topleft, dimensions)); + return variant2::visit(subimage_view_fn(topleft, dimensions), src); } /// \ingroup ImageViewTransformationsSubimage @@ -255,7 +256,7 @@ auto subimage_view( -> any_image_view { using subimage_view_fn = detail::subimage_view_fn>; - return apply_operation(src, subimage_view_fn(point_t(x_min, y_min),point_t(width, height))); + return variant2::visit(subimage_view_fn(point_t(x_min, y_min),point_t(width, height)), src); } /// \ingroup ImageViewTransformationsSubsampled @@ -267,7 +268,7 @@ auto subsampled_view(any_image_view const& src, point_t const& step) { using step_type = typename dynamic_xy_step_type>::type; using subsampled_view = detail::subsampled_view_fn; - return apply_operation(src, subsampled_view(step)); + return variant2::visit(subsampled_view(step), src); } /// \ingroup ImageViewTransformationsSubsampled @@ -279,7 +280,7 @@ auto subsampled_view(any_image_view const& src, std::ptrdiff_t x_step, { using step_type = typename dynamic_xy_step_type>::type; using subsampled_view_fn = detail::subsampled_view_fn; - return apply_operation(src, subsampled_view_fn(point_t(x_step, y_step))); + return variant2::visit(subsampled_view_fn(point_t(x_step, y_step)), src); } namespace detail { @@ -304,11 +305,11 @@ struct nth_channel_view_type> /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto nth_channel_view(const any_image_view& src, int n) +auto nth_channel_view(any_image_view const& src, int n) -> typename nth_channel_view_type>::type { using result_view_t = typename nth_channel_view_type>::type; - return apply_operation(src,detail::nth_channel_view_fn(n)); + return variant2::visit(detail::nth_channel_view_fn(n), src); } namespace detail { @@ -347,11 +348,11 @@ struct color_converted_view_type,DstP,CC> /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto color_converted_view(const any_image_view& src, CC) +auto color_converted_view(any_image_view const& src, CC) -> typename color_converted_view_type, DstP, CC>::type { using cc_view_t = typename color_converted_view_type, DstP, CC>::type; - return apply_operation(src, detail::color_converted_view_fn()); + return variant2::visit(detail::color_converted_view_fn(), src); } /// \ingroup ImageViewTransformationsColorConvert @@ -371,7 +372,7 @@ auto color_converted_view(any_image_view const& src) -> typename color_converted_view_type, DstP>::type { using cc_view_t = typename color_converted_view_type, DstP>::type; - return apply_operation(src, detail::color_converted_view_fn()); + return variant2::visit(detail::color_converted_view_fn(), src); } /// \ingroup ImageViewTransformationsColorConvert diff --git a/include/boost/gil/extension/io/bmp/detail/read.hpp b/include/boost/gil/extension/io/bmp/detail/read.hpp index b898662f6e..fa6ab2db98 100644 --- a/include/boost/gil/extension/io/bmp/detail/read.hpp +++ b/include/boost/gil/extension/io/bmp/detail/read.hpp @@ -725,8 +725,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + ,view( images ) ); } } diff --git a/include/boost/gil/extension/io/bmp/detail/write.hpp b/include/boost/gil/extension/io/bmp/detail/write.hpp index d82e1f2844..d75a6bdbbe 100644 --- a/include/boost/gil/extension/io/bmp/detail/write.hpp +++ b/include/boost/gil/extension/io/bmp/detail/write.hpp @@ -201,8 +201,8 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views - , op + variant2::visit( op + ,views ); } }; diff --git a/include/boost/gil/extension/io/jpeg/detail/read.hpp b/include/boost/gil/extension/io/jpeg/detail/read.hpp index 7ef0b5717b..57992c07e6 100644 --- a/include/boost/gil/extension/io/jpeg/detail/read.hpp +++ b/include/boost/gil/extension/io/jpeg/detail/read.hpp @@ -300,8 +300,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + , view( images ) ); } } diff --git a/include/boost/gil/extension/io/jpeg/detail/write.hpp b/include/boost/gil/extension/io/jpeg/detail/write.hpp index e3705616cf..e4534b3d58 100644 --- a/include/boost/gil/extension/io/jpeg/detail/write.hpp +++ b/include/boost/gil/extension/io/jpeg/detail/write.hpp @@ -167,7 +167,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/io/png/detail/read.hpp b/include/boost/gil/extension/io/png/detail/read.hpp index 62463d7557..089caf9fe6 100644 --- a/include/boost/gil/extension/io/png/detail/read.hpp +++ b/include/boost/gil/extension/io/png/detail/read.hpp @@ -432,8 +432,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + , view( images ) ); } } diff --git a/include/boost/gil/extension/io/png/detail/write.hpp b/include/boost/gil/extension/io/png/detail/write.hpp index f5cdf1cb67..5dcb6b698b 100644 --- a/include/boost/gil/extension/io/png/detail/write.hpp +++ b/include/boost/gil/extension/io/png/detail/write.hpp @@ -248,7 +248,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/io/pnm/detail/read.hpp b/include/boost/gil/extension/io/pnm/detail/read.hpp index 1ea53c7844..d25866bc86 100644 --- a/include/boost/gil/extension/io/pnm/detail/read.hpp +++ b/include/boost/gil/extension/io/pnm/detail/read.hpp @@ -437,8 +437,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + ,view( images ) ); } } diff --git a/include/boost/gil/extension/io/pnm/detail/write.hpp b/include/boost/gil/extension/io/pnm/detail/write.hpp index c6598ace45..c10d1f7955 100644 --- a/include/boost/gil/extension/io/pnm/detail/write.hpp +++ b/include/boost/gil/extension/io/pnm/detail/write.hpp @@ -233,7 +233,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/io/raw/detail/read.hpp b/include/boost/gil/extension/io/raw/detail/read.hpp index af2e7eea2f..c6fdb134f4 100644 --- a/include/boost/gil/extension/io/raw/detail/read.hpp +++ b/include/boost/gil/extension/io/raw/detail/read.hpp @@ -224,8 +224,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + , view( images ) ); } } diff --git a/include/boost/gil/extension/io/targa/detail/read.hpp b/include/boost/gil/extension/io/targa/detail/read.hpp index 8eec904db1..035d2d0b61 100644 --- a/include/boost/gil/extension/io/targa/detail/read.hpp +++ b/include/boost/gil/extension/io/targa/detail/read.hpp @@ -383,8 +383,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + ,view( images ) ); } } diff --git a/include/boost/gil/extension/io/targa/detail/write.hpp b/include/boost/gil/extension/io/targa/detail/write.hpp index 23d96bedad..cff5fb337e 100644 --- a/include/boost/gil/extension/io/targa/detail/write.hpp +++ b/include/boost/gil/extension/io/targa/detail/write.hpp @@ -170,7 +170,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/io/tiff/detail/read.hpp b/include/boost/gil/extension/io/tiff/detail/read.hpp index ccd695ff53..e0f40f412c 100644 --- a/include/boost/gil/extension/io/tiff/detail/read.hpp +++ b/include/boost/gil/extension/io/tiff/detail/read.hpp @@ -766,8 +766,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + , view( images ) ); } } diff --git a/include/boost/gil/extension/io/tiff/detail/write.hpp b/include/boost/gil/extension/io/tiff/detail/write.hpp index 9bc7b7c17e..cb10c1e9ee 100644 --- a/include/boost/gil/extension/io/tiff/detail/write.hpp +++ b/include/boost/gil/extension/io/tiff/detail/write.hpp @@ -438,7 +438,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/numeric/resample.hpp b/include/boost/gil/extension/numeric/resample.hpp index 16f859113f..aafcf2bd1d 100644 --- a/include/boost/gil/extension/numeric/resample.hpp +++ b/include/boost/gil/extension/numeric/resample.hpp @@ -73,10 +73,10 @@ namespace detail { template void resample_pixels(const any_image_view& src, const V2& dst, const MapFn& dst_to_src, Sampler sampler=Sampler()) { - apply_operation(src, std::bind( + variant2::visit(std::bind( detail::resample_pixels_fn(dst_to_src, sampler), std::placeholders::_1, - dst)); + dst), src); } /// \brief resample_pixels when the destination is run-time specified @@ -86,10 +86,10 @@ template void resample_pixels(const V1& src, const any_image_view& dst, const MapFn& dst_to_src, Sampler sampler=Sampler()) { using namespace std::placeholders; - apply_operation(dst, std::bind( + variant2::visit(std::bind( detail::resample_pixels_fn(dst_to_src, sampler), src, - std::placeholders::_1)); + std::placeholders::_1), dst); } /// \brief resample_pixels when both the source and the destination are run-time specified @@ -97,7 +97,7 @@ void resample_pixels(const V1& src, const any_image_view& dst, const /// \ingroup ImageAlgorithms template void resample_pixels(const any_image_view& src, const any_image_view& dst, const MapFn& dst_to_src, Sampler sampler=Sampler()) { - apply_operation(src,dst,detail::resample_pixels_fn(dst_to_src,sampler)); + variant2::visit(detail::resample_pixels_fn(dst_to_src,sampler), src, dst); } /////////////////////////////////////////////////////////////////////////// diff --git a/include/boost/gil/io/detail/dynamic.hpp b/include/boost/gil/io/detail/dynamic.hpp index 4df5fef2e9..239f370068 100644 --- a/include/boost/gil/io/detail/dynamic.hpp +++ b/include/boost/gil/io/detail/dynamic.hpp @@ -41,7 +41,7 @@ struct construct_matched_t<0> static bool apply(any_image&, Pred) { return false; } }; -// A function object that can be passed to apply_operation. +// A function object that can be passed to variant2::visit. // Given a predicate IsSupported taking a view type and returning an boolean integral coonstant, // calls the apply method of OpClass with the view if the given view IsSupported, or throws an exception otherwise template diff --git a/test/Jamfile b/test/Jamfile index 9583ac6ff0..6acff3979c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -26,10 +26,6 @@ local msvc-cxxs-with-experimental-fs = 11 14 ; project : requirements - . - # TODO: Enable concepts check for all, not just test/core - #BOOST_GIL_USE_CONCEPT_CHECK=1 - msvc,$(msvc-cxxs-with-experimental-fs):_SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING=1 [ requires cxx11_constexpr cxx11_defaulted_functions @@ -37,6 +33,11 @@ project cxx11_trailing_result_types # implies decltype and auto cxx11_variadic_templates ] + . + # TODO: Enable concepts check for all, not just test/core + #BOOST_GIL_USE_CONCEPT_CHECK=1 + msvc,$(msvc-cxxs-with-experimental-fs):_SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING=1 + msvc:/bigobj ; variant gil_ubsan_integer diff --git a/test/extension/dynamic_image/Jamfile b/test/extension/dynamic_image/Jamfile index 4d78b7554b..3f9e57bbbd 100644 --- a/test/extension/dynamic_image/Jamfile +++ b/test/extension/dynamic_image/Jamfile @@ -12,6 +12,7 @@ alias headers : [ generate_self_contained_headers extension/dynamic_image ] ; run any_image.cpp ; run any_image_view.cpp ; +run image_view_factory.cpp ; run subimage_view.cpp ; build-project algorithm ; diff --git a/test/extension/dynamic_image/algorithm/Jamfile b/test/extension/dynamic_image/algorithm/Jamfile index cdca8c65e8..071b29a56f 100644 --- a/test/extension/dynamic_image/algorithm/Jamfile +++ b/test/extension/dynamic_image/algorithm/Jamfile @@ -1,6 +1,6 @@ # Boost.GIL (Generic Image Library) - tests # -# Copyright (c) 2022 Marco Langer +# Copyright 2022 Marco Langer # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or @@ -8,4 +8,8 @@ import testing ; +run copy_and_convert_pixels.cpp ; +run copy_pixels.cpp ; +run equal_pixels.cpp ; +run fill_pixels.cpp ; run for_each_pixel.cpp ; diff --git a/test/extension/dynamic_image/algorithm/copy_and_convert_pixels.cpp b/test/extension/dynamic_image/algorithm/copy_and_convert_pixels.cpp new file mode 100644 index 0000000000..851b915551 --- /dev/null +++ b/test/extension/dynamic_image/algorithm/copy_and_convert_pixels.cpp @@ -0,0 +1,202 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under 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 "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_copy_and_convert_pixels +{ + template + void operator()(std::pair const&) + { + using image_lhs_t = ImageLhs; + using image_rhs_t = ImageRhs; + using image_view_lhs_t = typename image_lhs_t::view_t; + using image_view_rhs_t = typename image_rhs_t::view_t; + + gil::default_color_converter cc_default; + image_lhs_t image_lhs1(fixture::create_image(2, 2, 128)); + image_rhs_t image_rhs1(fixture::create_image(2, 2, 128)); + fixture::dynamic_image dyn_image_lhs1(image_lhs1); + fixture::dynamic_image dyn_image_rhs1(image_rhs1); + { + // dynamic_image -> image + image_lhs_t image_lhs2(2, 2); + image_lhs_t image_lhs3(2, 2); + image_rhs_t image_rhs2(2, 2); + image_rhs_t image_rhs3(2, 2); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(image_lhs2))); + + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(image_lhs3), cc_default)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(image_lhs3))); + + + // lhs, rhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(image_rhs2))); + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(image_rhs3), cc_default)); + + if (gil::views_are_compatible::value) + { + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_rhs1), + gil::const_view(image_rhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_rhs1), + gil::const_view(image_rhs3))); + } + } + { + // image -> dynamic_image + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_lhs3(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs2(image_rhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs3(image_rhs_t(2, 2)); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(image_lhs1), + gil::view(dyn_image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(dyn_image_lhs2))); + + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(image_lhs1), + gil::view(dyn_image_lhs3), cc_default)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(dyn_image_lhs3))); + + // lhs, rhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(image_lhs1), + gil::view(dyn_image_rhs2))); + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(image_lhs1), + gil::view(dyn_image_rhs3), cc_default)); + + if (gil::views_are_compatible::value) + { + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(dyn_image_rhs2))); + + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(dyn_image_rhs3))); + } + } + { + // dynamic_image -> dynamic_image + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_lhs3(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs2(image_rhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs3(image_rhs_t(2, 2)); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs2))); + + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_lhs3), cc_default)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs3))); + + // lhs, rhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_rhs2))); + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_rhs3), cc_default)); + + if (gil::views_are_compatible::value) + { + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_rhs2))); + + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_rhs3))); + } + } + } + + static void run() + { + boost::mp11::mp_for_each + < + boost::mp11::mp_pairwise_fold + < + fixture::image_types, std::pair + > + >(test_copy_and_convert_pixels{}); + } +}; + +int main() +{ + test_copy_and_convert_pixels::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/algorithm/copy_pixels.cpp b/test/extension/dynamic_image/algorithm/copy_pixels.cpp new file mode 100644 index 0000000000..40ad6eec59 --- /dev/null +++ b/test/extension/dynamic_image/algorithm/copy_pixels.cpp @@ -0,0 +1,163 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under 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 "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_copy_pixels +{ + template + void operator()(std::pair const&) + { + using image_lhs_t = ImageLhs; + using image_rhs_t = ImageRhs; + using image_view_lhs_t = typename image_lhs_t::view_t; + using image_view_rhs_t = typename image_rhs_t::view_t; + + { + // dynamic_image -> image + image_lhs_t image_lhs1(fixture::create_image(2, 2, 128)); + image_lhs_t image_lhs2(2, 2); + image_rhs_t image_rhs(2, 2); + fixture::dynamic_image dyn_image_lhs(image_lhs1); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(dyn_image_lhs), + gil::view(image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(image_lhs2))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(dyn_image_lhs), + gil::view(image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(image_rhs))); + } + else + { + BOOST_TEST_THROWS( + gil::copy_pixels( + gil::const_view(dyn_image_lhs), + gil::view(image_rhs)), + std::bad_cast); + } + } + { + // image -> dynamic_image + image_lhs_t image_lhs = fixture::create_image(2, 2, 128); + fixture::dynamic_image dyn_image_lhs1(image_lhs); + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs(image_rhs_t(2, 2)); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(image_lhs), + gil::view(dyn_image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs2))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(image_lhs), + gil::view(dyn_image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_rhs))); + } + else + { + BOOST_TEST_THROWS( + gil::copy_pixels( + gil::const_view(image_lhs), + gil::view(dyn_image_rhs)), + std::bad_cast); + } + } + { + // dynamic_image -> dynamic_image + fixture::dynamic_image dyn_image_lhs1(fixture::create_image(2, 2, 128)); + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs(image_rhs_t(2, 2)); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs2))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_rhs))); + } + else + { + BOOST_TEST_THROWS( + gil::copy_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_rhs)), + std::bad_cast); + } + } + } + + static void run() + { + boost::mp11::mp_for_each + < + boost::mp11::mp_pairwise_fold + < + fixture::image_types, std::pair + > + >(test_copy_pixels{}); + } +}; + +int main() +{ + test_copy_pixels::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/algorithm/equal_pixels.cpp b/test/extension/dynamic_image/algorithm/equal_pixels.cpp new file mode 100644 index 0000000000..eb9ef7440b --- /dev/null +++ b/test/extension/dynamic_image/algorithm/equal_pixels.cpp @@ -0,0 +1,104 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under 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 "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_equal_pixels +{ + template + void operator()(std::pair const&) + { + using image_lhs_t = ImageLhs; + using image_rhs_t = ImageRhs; + using image_view_lhs_t = typename image_lhs_t::view_t; + using image_view_rhs_t = typename image_rhs_t::view_t; + + image_lhs_t image_lhs = fixture::create_image(2, 2, 128); + image_rhs_t image_rhs = fixture::create_image(2, 2, 128); + + fixture::dynamic_image dyn_image_lhs(image_lhs); + fixture::dynamic_image dyn_image_rhs(image_rhs); + + // lhs, lhs + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(image_lhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs), + gil::const_view(dyn_image_lhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(dyn_image_lhs))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs), + gil::const_view(dyn_image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(dyn_image_rhs))); + } + else + { + BOOST_TEST_THROWS( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(image_rhs)), + std::bad_cast); + BOOST_TEST_THROWS( + gil::equal_pixels( + gil::const_view(image_lhs), + gil::const_view(dyn_image_rhs)), + std::bad_cast); + BOOST_TEST_THROWS( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(dyn_image_rhs)), + std::bad_cast); + } + } + + static void run() + { + boost::mp11::mp_for_each + < + boost::mp11::mp_pairwise_fold + < + fixture::image_types, std::pair + > + >(test_equal_pixels{}); + } +}; + +int main() +{ + test_equal_pixels::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/algorithm/fill_pixels.cpp b/test/extension/dynamic_image/algorithm/fill_pixels.cpp new file mode 100644 index 0000000000..31832a63e0 --- /dev/null +++ b/test/extension/dynamic_image/algorithm/fill_pixels.cpp @@ -0,0 +1,86 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under 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 "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_fill_pixels +{ + template + void operator()(std::pair const&) + { + using image_lhs_t = ImageLhs; + using image_rhs_t = ImageRhs; + using image_view_lhs_t = typename image_lhs_t::view_t; + using image_view_rhs_t = typename image_rhs_t::view_t; + using value_lhs_t = typename image_lhs_t::value_type; + using value_rhs_t = typename image_rhs_t::value_type; + + image_lhs_t image_lhs = fixture::create_image(2, 2, 128); + fixture::dynamic_image dyn_image_lhs1(image_lhs); + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_lhs3(image_lhs_t(2, 2)); + value_lhs_t value_lhs(128); + value_rhs_t value_rhs(128); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::fill_pixels( + gil::view(dyn_image_lhs2), value_lhs)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs2))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST_NO_THROW( + gil::fill_pixels( + gil::view(dyn_image_lhs3), value_rhs)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs3))); + } + else + { + BOOST_TEST_THROWS( + gil::fill_pixels( + gil::view(dyn_image_lhs3), value_rhs), + std::bad_cast); + } + } + + static void run() + { + boost::mp11::mp_for_each + < + boost::mp11::mp_pairwise_fold + < + fixture::image_types, std::pair + > + >(test_fill_pixels{}); + } +}; + +int main() +{ + test_fill_pixels::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/algorithm/for_each_pixel.cpp b/test/extension/dynamic_image/algorithm/for_each_pixel.cpp index 67a947cac2..ad5620a5bf 100644 --- a/test/extension/dynamic_image/algorithm/for_each_pixel.cpp +++ b/test/extension/dynamic_image/algorithm/for_each_pixel.cpp @@ -1,17 +1,17 @@ // -// Copyright 2022 Marco Langer +// Copyright 2022 Marco Langer // // Distributed under 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 "../test_fixture.hpp" #include "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" namespace gil = boost::gil; namespace fixture = boost::gil::test::fixture; @@ -49,4 +49,4 @@ int main() test_for_each_pixel::run(); return ::boost::report_errors(); -} \ No newline at end of file +} diff --git a/test/extension/dynamic_image/any_image.cpp b/test/extension/dynamic_image/any_image.cpp index 68ead86bba..7dfe28e495 100644 --- a/test/extension/dynamic_image/any_image.cpp +++ b/test/extension/dynamic_image/any_image.cpp @@ -22,9 +22,12 @@ struct test_any_image_move_ctor void operator()(Image const&) { using image_t = Image; - fixture::dynamic_image i0(fixture::create_image(4, 4, 128)); + fixture::dynamic_image i0(fixture::create_image(4, 5, 128)); BOOST_TEST_EQ(i0.dimensions().x, 4); - BOOST_TEST_EQ(i0.dimensions().y, 4); + BOOST_TEST_EQ(i0.dimensions().y, 5); + BOOST_TEST_EQ(i0.width(), 4); + BOOST_TEST_EQ(i0.height(), 5); + BOOST_TEST_EQ(i0.num_channels(), gil::num_channels::value); fixture::dynamic_image i1 = fixture::create_image(4, 4, 128); BOOST_TEST_EQ(i1.dimensions().x, 4); @@ -36,10 +39,29 @@ struct test_any_image_move_ctor } }; +struct test_any_image_recreate +{ + template + void operator()(Image const&) + { + using image_t = Image; + using point_t = typename image_t::point_t; + fixture::dynamic_image image(image_t(4, 5, 128)); + point_t point(5, 6); + + BOOST_TEST_NO_THROW(image.recreate(point)); + } + + static void run() + { + boost::mp11::mp_for_each(test_any_image_recreate{}); + } +}; int main() { test_any_image_move_ctor::run(); + test_any_image_recreate::run(); return ::boost::report_errors(); } diff --git a/test/extension/dynamic_image/any_image_view.cpp b/test/extension/dynamic_image/any_image_view.cpp index 176e7f25b2..fc215e0379 100644 --- a/test/extension/dynamic_image/any_image_view.cpp +++ b/test/extension/dynamic_image/any_image_view.cpp @@ -34,18 +34,22 @@ struct test_any_image_view_init_ctor using view_t = typename Image::view_t; using any_view_t = gil::any_image_view; using any_const_view_t = typename any_view_t::const_t; - Image i0(fixture::create_image(4, 4, 128)); + Image i0(fixture::create_image(4, 5, 128)); view_t v0 = gil::view(i0); any_view_t v1 = v0; BOOST_TEST_EQ(v1.dimensions().x, 4); - BOOST_TEST_EQ(v1.dimensions().y, 4); + BOOST_TEST_EQ(v1.dimensions().y, 5); + BOOST_TEST_EQ(v1.width(), 4); + BOOST_TEST_EQ(v1.height(), 5); + BOOST_TEST_EQ(v1.size(), 4 * 5); + BOOST_TEST_EQ(v1.num_channels(), gil::num_channels::value); any_const_view_t v2 = v0; BOOST_TEST_EQ(v2.dimensions().x, 4); - BOOST_TEST_EQ(v2.dimensions().y, 4); + BOOST_TEST_EQ(v2.dimensions().y, 5); //any_const_view_t v3 = v1; } diff --git a/test/extension/dynamic_image/image_view_factory.cpp b/test/extension/dynamic_image/image_view_factory.cpp new file mode 100644 index 0000000000..ce0ad9ee28 --- /dev/null +++ b/test/extension/dynamic_image/image_view_factory.cpp @@ -0,0 +1,92 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under 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 "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +template +struct generator +{ + generator(int const* data) : data_(data) {} + + auto operator()() -> int + { + if(++i_ == Channels) { + i_ = 0; + return *data_++; + } + else + { + return *data_; + } + } + + int i_= 0; + int const* data_; +}; + +struct test_flipped_up_down_view +{ + template + void operator()(Image const&) + { + using image_t = Image; + using pixel_t = typename image_t::value_type; + static constexpr std::size_t num_channels = gil::num_channels::value; + + std::array pixel_data = + { + 0, 1, 2, + 3, 4, 5, + 6, 7, 8 + }; + std::array expected_pixel_data = + { + 6, 7, 8, + 3, 4, 5, + 0, 1, 2 + }; + + fixture::dynamic_image source_image = + fixture::generate_image( + 3, 3, generator{pixel_data.data()}); + + fixture::dynamic_image expected_image = + fixture::generate_image( + 3, 3, generator{expected_pixel_data.data()}); + + auto result_view = gil::flipped_up_down_view(gil::const_view(source_image)); + + BOOST_TEST( + gil::equal_pixels( + result_view, + gil::const_view(expected_image))); + } + + static void run() + { + boost::mp11::mp_for_each(test_flipped_up_down_view{}); + } +}; + + +int main() +{ + test_flipped_up_down_view::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/subimage_view.cpp b/test/extension/dynamic_image/subimage_view.cpp index b1e22ae5f9..37cca2c6d7 100644 --- a/test/extension/dynamic_image/subimage_view.cpp +++ b/test/extension/dynamic_image/subimage_view.cpp @@ -15,6 +15,7 @@ #include "core/image/test_fixture.hpp" namespace gil = boost::gil; +namespace variant2 = boost::variant2; namespace fixture = boost::gil::test::fixture; struct test_subimage_equals_image @@ -61,16 +62,16 @@ struct test_subimage_equals_image_quadrants // create test image and set values of pixels in: // quadrant 1 auto const i1 = fixture::create_image(2, 2, 255); - gil::apply_operation(v0, fixture::fill_any_view({2, 3, 6, 7}, gil::const_view(i1)[0])); + variant2::visit(fixture::fill_any_view({2, 3, 6, 7}, gil::const_view(i1)[0]), v0); // quadrant 2 auto const i2 = fixture::create_image(2, 2, 128); - gil::apply_operation(v0, fixture::fill_any_view({0, 1, 4, 5}, gil::const_view(i2)[0])); + variant2::visit(fixture::fill_any_view({0, 1, 4, 5}, gil::const_view(i2)[0]), v0); // quadrant 3 auto const i3 = fixture::create_image(2, 2, 64); - gil::apply_operation(v0, fixture::fill_any_view({8, 9, 12, 13}, gil::const_view(i3)[0])); + variant2::visit(fixture::fill_any_view({8, 9, 12, 13}, gil::const_view(i3)[0]), v0); // quadrant 4 auto const i4 = fixture::create_image(2, 2, 32); - gil::apply_operation(v0, fixture::fill_any_view({10, 11, 14, 15}, gil::const_view(i4)[0])); + variant2::visit(fixture::fill_any_view({10, 11, 14, 15}, gil::const_view(i4)[0]), v0); auto v1 = gil::subimage_view(gil::view(i0), { 2, 0 }, i0.dimensions() / 2); BOOST_TEST(gil::equal_pixels(v1, gil::const_view(i1))); From adddbec8961a152db5751802307cc4ee977fd15d Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sun, 26 Jun 2022 13:33:53 +0200 Subject: [PATCH 136/193] refactor: Ellipse rasterizer according to the comment at (#692) https://github.com/boostorg/gil/pull/585#issuecomment-1144412220 --- example/rasterizer_circle.cpp | 5 +- example/rasterizer_ellipse.cpp | 30 ++-- example/rasterizer_line.cpp | 4 +- .../gil/extension/rasterization/ellipse.hpp | 147 ++++++++---------- test/extension/rasterization/ellipse.cpp | 14 +- 5 files changed, 95 insertions(+), 105 deletions(-) diff --git a/example/rasterizer_circle.cpp b/example/rasterizer_circle.cpp index 2c56de3199..0ab36ddf0f 100644 --- a/example/rasterizer_circle.cpp +++ b/example/rasterizer_circle.cpp @@ -8,6 +8,7 @@ #include #include + #include #include #include @@ -15,8 +16,8 @@ namespace gil = boost::gil; // Demonstrates the use of a rasterizer to generate an image of a circle -// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, -// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// The various rasterizers available are defined in include/boost/gil/extension/rasterization/circle.hpp, +// include/boost/gil/extension/rasterization/ellipse.hpp and include/boost/gil/extension/rasterization/line.hpp // This example uses a trigonometric rasterizer; GIL also offers the rasterizer midpoint_circle_rasterizer, // which implements the Midpoint algorithm. // See also: diff --git a/example/rasterizer_ellipse.cpp b/example/rasterizer_ellipse.cpp index 295fe0533c..d34ba53635 100644 --- a/example/rasterizer_ellipse.cpp +++ b/example/rasterizer_ellipse.cpp @@ -6,14 +6,14 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#include #include +#include namespace gil = boost::gil; // Demonstrates the use of a rasterizer to generate an image of an ellipse -// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, -// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// The various rasterizers available are defined in include/boost/gil/extension/rasterization/circle.hpp, +// include/boost/gil/extension/rasterization/ellipse.hpp and include/boost/gil/extension/rasterization/line.hpp // The rasterizer used is a generalisation of the midpoint algorithm often used for drawing circle. // This examples also shows how to create images with various pixel depth, as well as the behaviour // in case of the rasterization of a curve that doesn't fit in a view. @@ -25,28 +25,28 @@ namespace gil = boost::gil; int main() { // Syntax for usage :- - // auto rasterizer = gil::midpoint_elliptical_rasterizer{}; - // rasterizer(img_view, colour, center, semi-axes_length); + // auto rasterizer = gil::midpoint_ellipse_rasterizer{}; + // rasterizer(img_view, pixel, center, semi-axes_length); // Where // img_view : gil view of the image on which ellipse is to be drawn. - // colour : Vector containing channel intensity values for img_view. Number of colours - // provided must be equal to the number of channels present in img_view. - // center : Array containing positive integer x co-ordinate and y co-ordinate of the center + // pixel : Pixel value for the elliptical curve to be drawn. Pixel's type must be compatible to the + // pixel type of the image view. + // center : Point containing positive integer x co-ordinate and y co-ordinate of the center // respectively. - // semi-axes_length : Array containing positive integer lengths of horizontal semi-axis + // semi-axes_length : Point containing positive integer lengths of horizontal semi-axis // and vertical semi-axis respectively. gil::gray8_image_t gray_buffer_image(256, 256); - auto gray_elliptical_rasterizer = gil::midpoint_elliptical_rasterizer{}; - gray_elliptical_rasterizer(view(gray_buffer_image), {128}, {128, 128}, {100, 50}); + auto gray_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{}; + gray_ellipse_rasterizer(view(gray_buffer_image), gil::gray8_pixel_t{128}, {128, 128}, {100, 50}); gil::rgb8_image_t rgb_buffer_image(256, 256); - auto rgb_elliptical_rasterizer = gil::midpoint_elliptical_rasterizer{}; - rgb_elliptical_rasterizer(view(rgb_buffer_image), {0, 0, 255}, {128, 128}, {50, 100}); + auto rgb_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{}; + rgb_ellipse_rasterizer(view(rgb_buffer_image), gil::rgb8_pixel_t{0, 0, 255}, {128, 128}, {50, 100}); gil::rgb8_image_t rgb_buffer_image_out_of_bound(256, 256); - auto rgb_elliptical_rasterizer_out_of_bound = gil::midpoint_elliptical_rasterizer{}; - rgb_elliptical_rasterizer_out_of_bound(view(rgb_buffer_image_out_of_bound), {255, 0, 0}, + auto rgb_ellipse_rasterizer_out_of_bound = gil::midpoint_ellipse_rasterizer{}; + rgb_ellipse_rasterizer_out_of_bound(view(rgb_buffer_image_out_of_bound), gil::rgb8_pixel_t{255, 0, 0}, {100, 100}, {160, 160}); gil::write_view("rasterized_ellipse_gray.jpg", view(gray_buffer_image), gil::jpeg_tag{}); diff --git a/example/rasterizer_line.cpp b/example/rasterizer_line.cpp index 7b2aec35b1..026ad10d9a 100644 --- a/example/rasterizer_line.cpp +++ b/example/rasterizer_line.cpp @@ -15,8 +15,8 @@ namespace gil = boost::gil; // Demonstrates the use of a rasterizer to generate an image of a line -// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, -// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// The various rasterizers available are defined in include/boost/gil/extension/rasterization/circle.hpp, +// include/boost/gil/extension/rasterization/ellipse.hpp and include/boost/gil/extension/rasterization/line.hpp // The rasterizer used implements the Bresenham's line algorithm. // Multiple images are created, all of the same size, but with areas of different sizes passed to the rasterizer, resulting in different lines. // See also: diff --git a/include/boost/gil/extension/rasterization/ellipse.hpp b/include/boost/gil/extension/rasterization/ellipse.hpp index 6ea6ea46cb..2f9aca9442 100644 --- a/include/boost/gil/extension/rasterization/ellipse.hpp +++ b/include/boost/gil/extension/rasterization/ellipse.hpp @@ -8,9 +8,12 @@ #ifndef BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP #define BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP +#include +#include + #include +#include #include -#include namespace boost { namespace gil { @@ -22,21 +25,21 @@ namespace boost { namespace gil { /// \brief Performs ellipse rasterization using midpoint algorithm. Initially, program considers /// origin as center of ellipse and obtains first quadrant trajectory points. After that, /// it shifts origin to provided co-ordinates of center and then draws the curve. -struct midpoint_elliptical_rasterizer +struct midpoint_ellipse_rasterizer { /// \brief Returns a vector containing co-ordinates of first quadrant points which lie on /// rasterizer trajectory of the ellipse. - /// \param semi_axes - Array containing half of lengths of horizontal and vertical axis + /// \param semi_axes - Point containing half of lengths of horizontal and vertical axis /// respectively. - auto obtain_trajectory(std::array const semi_axes) - -> std::vector> + auto obtain_trajectory(point semi_axes) + -> std::vector { // Citation : J. Van Aken, "An Efficient Ellipse-Drawing Algorithm" in IEEE Computer // Graphics and Applications, vol. 4, no. 09, pp. 24-35, 1984. // doi: 10.1109/MCG.1984.275994 // keywords: {null} // url: https://doi.ieeecomputersociety.org/10.1109/MCG.1984.275994 - std::vector> trajectory_points; + std::vector trajectory_points; std::ptrdiff_t x = semi_axes[0], y = 0; // Variables declared on following lines are temporary variables used for improving @@ -96,58 +99,63 @@ struct midpoint_elliptical_rasterizer /// obtained from their reflection along major axis, minor axis and line passing through /// center with slope -1 using colours provided by user. /// \param view - Gil view of image on which the elliptical curve is to be drawn. - /// \param colour - Constant vector specifying colour intensity values for all channels present - /// in 'view'. - /// \param center - Constant array specifying co-ordinates of center of ellipse to be drawn. + /// \param pixel - Pixel value for the elliptical curve to be drawn. + /// \param center - Point specifying co-ordinates of center of ellipse to be drawn. /// \param trajectory_points - Constant vector specifying pixel co-ordinates of points lying /// on rasterizer trajectory. /// \tparam View - Type of input image view. - template - void draw_curve(View view, std::vector const colour, - std::array const center, - std::vector> const trajectory_points) + /// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view + template + void draw_curve(View& view, Pixel const& pixel, + point center, + std::vector const& trajectory_points) { - for (int i = 0, colour_index = 0; i < static_cast(view.num_channels()); - ++i, ++colour_index) + using pixel_t = typename View::value_type; + if (!pixels_are_compatible()) { - for (std::array point : trajectory_points) + throw std::runtime_error("Pixel type of the given image is not compatible to the " + "type of the provided pixel."); + } + + --center[0], --center[1]; // For converting center co-ordinate values to zero based indexing. + for (point_t pnt : trajectory_points) + { + std::array co_ords = {center[0] + pnt[0], + center[0] - pnt[0], center[1] + pnt[1], center[1] - pnt[1] + }; + bool validity[4]{}; + if (co_ords[0] < view.width()) + { + validity[0] = true; + } + if (co_ords[1] >= 0 && co_ords[1] < view.width()) { - std::array co_ords = {center[0] + point[0], - center[0] - point[0], center[1] + point[1], center[1] - point[1] - }; - bool validity[4] = {0}; - if (co_ords[0] < view.width()) - { - validity[0] = 1; - } - if (co_ords[1] >= 0 && co_ords[1] < view.width()) - { - validity[1] = 1; - } - if (co_ords[2] < view.height()) - { - validity[2] = 1; - } - if (co_ords[3] >= 0 && co_ords[3] < view.height()) - { - validity[3] = 1; - } - if (validity[0] && validity[2]) - { - nth_channel_view(view, i)(co_ords[0], co_ords[2])[0] = colour[colour_index]; - } - if (validity[1] && validity[2]) - { - nth_channel_view(view, i)(co_ords[1], co_ords[2])[0] = colour[colour_index]; - } - if (validity[1] && validity[3]) - { - nth_channel_view(view, i)(co_ords[1], co_ords[3])[0] = colour[colour_index]; - } - if (validity[0] && validity[3]) - { - nth_channel_view(view, i)(co_ords[0], co_ords[3])[0] = colour[colour_index]; - } + validity[1] = true; + } + if (co_ords[2] < view.height()) + { + validity[2] = true; + } + if (co_ords[3] >= 0 && co_ords[3] < view.height()) + { + validity[3] = true; + } + + if (validity[0] && validity[2]) + { + view(co_ords[0], co_ords[2]) = pixel; + } + if (validity[1] && validity[2]) + { + view(co_ords[1], co_ords[2]) = pixel; + } + if (validity[1] && validity[3]) + { + view(co_ords[1], co_ords[3]) = pixel; + } + if (validity[0] && validity[3]) + { + view(co_ords[0], co_ords[3]) = pixel; } } } @@ -155,37 +163,18 @@ struct midpoint_elliptical_rasterizer /// \brief Calls the function 'obtain_trajectory' and then passes obtained trajectory points /// in the function 'draw_curve' for drawing the desired ellipse. /// \param view - Gil view of image on which the elliptical curve is to be drawn. - /// \param colour - Constant vector specifying colour intensity values for all channels present - /// in 'view'. - /// \param center - Array containing positive integer x co-ordinate and y co-ordinate of the + /// \param pixel - Pixel value for the elliptical curve to be drawn. + /// \param center - Point containing positive integer x co-ordinate and y co-ordinate of the /// center respectively. - /// \param semi_axes - Array containing positive integer lengths of horizontal semi-axis + /// \param semi_axes - Point containing positive integer lengths of horizontal semi-axis /// and vertical semi-axis respectively. /// \tparam View - Type of input image view. - template - void operator()(View view, std::vector const colour, - std::array center, std::array const semi_axes) + /// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view + template + void operator()(View& view, Pixel const& pixel, + point center, point semi_axes) { - --center[0], --center[1]; // For converting center co-ordinate values to zero based indexing. - if (colour.size() != view.num_channels()) - { - throw std::length_error("Number of channels in given image is not equal to the " - "number of colours provided."); - } - if (center[0] + semi_axes[0] >= view.width() || center[1] + semi_axes[1] >= view.height() - || static_cast(center[0] - semi_axes[0]) < 0 - || static_cast(center[0] - semi_axes[0]) >= view.width() - || static_cast(center[1] - semi_axes[1]) < 0 - || static_cast(center[1] - semi_axes[1]) >= view.height()) - { - std::cout << "Image can't contain whole curve.\n" - "However, it will contain those parts of curve which can fit inside it.\n" - "Note : Image width = " << view.width() << " and Image height = " << - view.height() << "\n"; - } - std::vector> trajectory_points = - obtain_trajectory(semi_axes); - draw_curve(view, colour, center, trajectory_points); + draw_curve(view, pixel, center, obtain_trajectory(semi_axes)); } }; diff --git a/test/extension/rasterization/ellipse.cpp b/test/extension/rasterization/ellipse.cpp index 495e40400a..1b44707917 100644 --- a/test/extension/rasterization/ellipse.cpp +++ b/test/extension/rasterization/ellipse.cpp @@ -6,8 +6,8 @@ // http://www.boost.org/LICENSE_1_0.txt) // #include -#include "boost/gil.hpp" -#include +#include + #include #include @@ -17,7 +17,7 @@ namespace gil = boost::gil; // is equal to the length of major axis of the ellipse. // Parameters b and a represent half of lengths of vertical and horizontal axis respectively. void test_rasterizer_follows_equation( - std::vector> trajectory_points, float a, float b) + std::vector const& trajectory_points, float a, float b) { float focus_x, focus_y; if (a > b) // For horizontal ellipse @@ -34,7 +34,7 @@ void test_rasterizer_follows_equation( for (auto trajectory_point : trajectory_points) { // To suppress conversion warnings from compiler - std::array point { + gil::point point { static_cast(trajectory_point[0]), static_cast(trajectory_point[1])}; double dist_sum = std::sqrt(std::pow(focus_x - point[0], 2) + @@ -53,7 +53,7 @@ void test_rasterizer_follows_equation( // This function verifies that the difference between x co-ordinates and y co-ordinates for two // successive trajectory points is less than or equal to 1. This ensures that the curve is connected. -void test_connectivity(std::vector> points) +void test_connectivity(std::vector const& points) { for (std::size_t i = 1; i < points.size(); ++i) { @@ -72,8 +72,8 @@ int main() { for (float b = 1; b < 101; ++b) { - auto rasterizer = gil::midpoint_elliptical_rasterizer{}; - std::vector> points = rasterizer.obtain_trajectory( + auto rasterizer = gil::midpoint_ellipse_rasterizer{}; + std::vector points = rasterizer.obtain_trajectory( {static_cast(a), static_cast(b)}); test_rasterizer_follows_equation(points, a, b); test_connectivity(points); From d5492e1ace19e7716e525323f8e18d14a0d9fb9a Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Mon, 27 Jun 2022 09:54:58 +0200 Subject: [PATCH 137/193] feat: Added apply_rasterizer() free function (#695) This PR implements the gil::apply_rasterizer() free function mentioned in #680. --- example/hough_transform_circle.cpp | 10 +- example/rasterizer_circle.cpp | 15 +-- example/rasterizer_ellipse.cpp | 13 +-- example/rasterizer_line.cpp | 15 +-- .../image_processing/hough_transform.hpp | 7 +- .../rasterization/apply_rasterizer.hpp | 28 +++++ .../gil/extension/rasterization/circle.hpp | 106 +++++++++++++----- .../gil/extension/rasterization/ellipse.hpp | 58 +++++++--- .../gil/extension/rasterization/line.hpp | 53 +++++++-- .../hough_circle_transform.cpp | 15 +-- .../image_processing/hough_line_transform.cpp | 7 +- test/extension/rasterization/circle.cpp | 22 ++-- test/extension/rasterization/ellipse.cpp | 6 +- test/extension/rasterization/line.cpp | 6 +- 14 files changed, 240 insertions(+), 121 deletions(-) create mode 100644 include/boost/gil/extension/rasterization/apply_rasterizer.hpp diff --git a/example/hough_transform_circle.cpp b/example/hough_transform_circle.cpp index 1e9d25798d..44a739d62d 100644 --- a/example/hough_transform_circle.cpp +++ b/example/hough_transform_circle.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -33,13 +34,8 @@ int main() const std::ptrdiff_t circle_radius = 16; const gil::point_t circle_center = {64, 64}; - const auto rasterizer = gil::midpoint_circle_rasterizer{}; - std::vector circle_points(rasterizer.point_count(circle_radius)); - rasterizer(circle_radius, circle_center, circle_points.begin()); - for (const auto& point : circle_points) - { - input(point) = std::numeric_limits::max(); - } + const auto rasterizer = gil::midpoint_circle_rasterizer{circle_center, circle_radius}; + gil::apply_rasterizer(input, rasterizer, gil::gray8_pixel_t{255}); const auto radius_parameter = gil::hough_parameter::from_step_count(circle_radius, 3, 3); diff --git a/example/rasterizer_circle.cpp b/example/rasterizer_circle.cpp index 0ab36ddf0f..ae85e86447 100644 --- a/example/rasterizer_circle.cpp +++ b/example/rasterizer_circle.cpp @@ -8,10 +8,7 @@ #include #include - -#include -#include -#include +#include namespace gil = boost::gil; @@ -30,14 +27,10 @@ int main() gil::gray8_image_t buffer_image(size, size); auto buffer = gil::view(buffer_image); + const gil::point_t center = {128, 128}; const std::ptrdiff_t radius = 64; - const auto rasterizer = gil::trigonometric_circle_rasterizer{}; - std::vector circle_points(rasterizer.point_count(radius)); - rasterizer(radius, {128, 128}, circle_points.begin()); - for (const auto& point : circle_points) - { - buffer(point) = std::numeric_limits::max(); - } + const auto rasterizer = gil::trigonometric_circle_rasterizer{center, radius}; + gil::apply_rasterizer(buffer, rasterizer, gil::gray8_pixel_t{255}); gil::write_view("circle.png", buffer, gil::png_tag{}); } diff --git a/example/rasterizer_ellipse.cpp b/example/rasterizer_ellipse.cpp index d34ba53635..91ff538930 100644 --- a/example/rasterizer_ellipse.cpp +++ b/example/rasterizer_ellipse.cpp @@ -37,17 +37,16 @@ int main() // and vertical semi-axis respectively. gil::gray8_image_t gray_buffer_image(256, 256); - auto gray_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{}; - gray_ellipse_rasterizer(view(gray_buffer_image), gil::gray8_pixel_t{128}, {128, 128}, {100, 50}); + auto gray_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{{128, 128}, {100, 50}}; + gil::apply_rasterizer(view(gray_buffer_image), gray_ellipse_rasterizer, gil::gray8_pixel_t{128}); gil::rgb8_image_t rgb_buffer_image(256, 256); - auto rgb_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{}; - rgb_ellipse_rasterizer(view(rgb_buffer_image), gil::rgb8_pixel_t{0, 0, 255}, {128, 128}, {50, 100}); + auto rgb_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{{128, 128}, {50, 100}}; + gil::apply_rasterizer(view(rgb_buffer_image), rgb_ellipse_rasterizer, gil::rgb8_pixel_t{0, 0, 255}); gil::rgb8_image_t rgb_buffer_image_out_of_bound(256, 256); - auto rgb_ellipse_rasterizer_out_of_bound = gil::midpoint_ellipse_rasterizer{}; - rgb_ellipse_rasterizer_out_of_bound(view(rgb_buffer_image_out_of_bound), gil::rgb8_pixel_t{255, 0, 0}, - {100, 100}, {160, 160}); + auto rgb_ellipse_rasterizer_out_of_bound = gil::midpoint_ellipse_rasterizer{{100, 100}, {160, 160}}; + apply_rasterizer(view(rgb_buffer_image_out_of_bound), rgb_ellipse_rasterizer_out_of_bound, gil::rgb8_pixel_t{255, 0, 0}); gil::write_view("rasterized_ellipse_gray.jpg", view(gray_buffer_image), gil::jpeg_tag{}); gil::write_view("rasterized_ellipse_rgb.jpg", view(rgb_buffer_image), gil::jpeg_tag{}); diff --git a/example/rasterizer_line.cpp b/example/rasterizer_line.cpp index 026ad10d9a..6c217023b5 100644 --- a/example/rasterizer_line.cpp +++ b/example/rasterizer_line.cpp @@ -8,9 +8,7 @@ #include #include - -#include -#include +#include namespace gil = boost::gil; @@ -28,18 +26,11 @@ const std::ptrdiff_t size = 256; void line_bresenham(std::ptrdiff_t width, std::ptrdiff_t height, const std::string& output_name) { - const auto rasterizer = gil::bresenham_line_rasterizer{}; - std::vector line_points(rasterizer.point_count(width, height)); + const auto rasterizer = gil::bresenham_line_rasterizer{{0, 0}, {width - 1, height - 1}}; gil::gray8_image_t image(size, size); auto view = gil::view(image); - - rasterizer({0, 0}, {width - 1, height - 1}, line_points.begin()); - for (const auto& point : line_points) - { - view(point) = std::numeric_limits::max(); - } - + gil::apply_rasterizer(view, rasterizer, gil::gray8_pixel_t{255}); gil::write_view(output_name, view, gil::png_tag{}); } diff --git a/include/boost/gil/extension/image_processing/hough_transform.hpp b/include/boost/gil/extension/image_processing/hough_transform.hpp index f0dada5fea..db63fbe837 100644 --- a/include/boost/gil/extension/image_processing/hough_transform.hpp +++ b/include/boost/gil/extension/image_processing/hough_transform.hpp @@ -86,14 +86,15 @@ void hough_circle_transform_brute(const ImageView& input, const hough_parameter radius_parameter, const hough_parameter x_parameter, const hough_parameter& y_parameter, - ForwardIterator d_first, Rasterizer rasterizer) + ForwardIterator d_first, Rasterizer) { for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; ++radius_index) { const auto radius = radius_parameter.start_point + radius_parameter.step_size * static_cast(radius_index); - std::vector circle_points(rasterizer.point_count(radius)); - rasterizer(radius, {0, 0}, circle_points.begin()); + Rasterizer rasterizer{point_t{}, radius}; + std::vector circle_points(rasterizer.point_count()); + rasterizer(circle_points.begin()); // sort by scanline to improve cache coherence for row major images std::sort(circle_points.begin(), circle_points.end(), [](const point_t& lhs, const point_t& rhs) { return lhs.y < rhs.y; }); diff --git a/include/boost/gil/extension/rasterization/apply_rasterizer.hpp b/include/boost/gil/extension/rasterization/apply_rasterizer.hpp new file mode 100644 index 0000000000..4eaffc35c7 --- /dev/null +++ b/include/boost/gil/extension/rasterization/apply_rasterizer.hpp @@ -0,0 +1,28 @@ +#ifndef BOOST_GIL_EXTENSION_RASTERIZATION_APPLY_RASTERIZER +#define BOOST_GIL_EXTENSION_RASTERIZATION_APPLY_RASTERIZER + +namespace boost { namespace gil { + +namespace detail { + +template +struct apply_rasterizer_op +{ + void operator()( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel); +}; + +} // namespace detail + +template +void apply_rasterizer( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel) +{ + using tag_t = typename Rasterizer::type; + detail::apply_rasterizer_op{}( + view, rasterizer, pixel); +} + +}} // namespace boost::gil + +#endif diff --git a/include/boost/gil/extension/rasterization/circle.hpp b/include/boost/gil/extension/rasterization/circle.hpp index 52629741fa..2fe9a35c21 100644 --- a/include/boost/gil/extension/rasterization/circle.hpp +++ b/include/boost/gil/extension/rasterization/circle.hpp @@ -9,13 +9,17 @@ #define BOOST_GIL_EXTENSION_RASTERIZATION_CIRCLE_HPP #include +#include #include #include #include +#include namespace boost { namespace gil { +struct circle_rasterizer_t{}; + /// \defgroup CircleRasterization /// \ingroup Rasterization /// \brief Circle rasterization algorithms @@ -32,39 +36,49 @@ namespace boost { namespace gil { /// produces quite round like shapes. struct trigonometric_circle_rasterizer { + using type = circle_rasterizer_t; + + /// \brief Creates a trigonometric circle rasterizer + /// \param center_point - Point containing positive integer x co-ordinate and y co-ordinate of the + /// center respectively. + /// \param circle_radius - Radius of the circle + trigonometric_circle_rasterizer(point_t center_point, std::ptrdiff_t circle_radius) + : center(center_point), radius(circle_radius) + {} + /// \brief Calculates minimum angle step that is distinguishable when walking on circle /// /// It is important to not have disconnected circle and to not compute unnecessarily, /// thus the result of this function is used when rendering. - double minimum_angle_step(std::ptrdiff_t radius) const noexcept + double minimum_angle_step() const noexcept { const auto diameter = radius * 2 - 1; return std::atan2(1.0, diameter); } /// \brief Calculate the amount of points that rasterizer will output - std::ptrdiff_t point_count(std::ptrdiff_t radius) const noexcept + std::ptrdiff_t point_count() const noexcept { return 8 * static_cast( - std::round(detail::pi / 4 / minimum_angle_step(radius)) + 1); + std::round(detail::pi / 4 / minimum_angle_step()) + 1); } /// \brief perform rasterization and output into d_first - template - void operator()(std::ptrdiff_t radius, point_t offset, RandomAccessIterator d_first) const + template + void operator()(OutputIterator d_first) const { const double minimum_angle_step = std::atan2(1.0, radius); - auto translate_mirror_points = [&d_first, offset](point_t p) { - *d_first++ = point_t{offset.x + p.x, offset.y + p.y}; - *d_first++ = point_t{offset.x + p.x, offset.y - p.y}; - *d_first++ = point_t{offset.x - p.x, offset.y + p.y}; - *d_first++ = point_t{offset.x - p.x, offset.y - p.y}; - *d_first++ = point_t{offset.x + p.y, offset.y + p.x}; - *d_first++ = point_t{offset.x + p.y, offset.y - p.x}; - *d_first++ = point_t{offset.x - p.y, offset.y + p.x}; - *d_first++ = point_t{offset.x - p.y, offset.y - p.x}; + auto translate_mirror_points = [this, &d_first](point_t p) { + *d_first++ = point_t{center.x + p.x, center.y + p.y}; + *d_first++ = point_t{center.x + p.x, center.y - p.y}; + *d_first++ = point_t{center.x - p.x, center.y + p.y}; + *d_first++ = point_t{center.x - p.x, center.y - p.y}; + *d_first++ = point_t{center.x + p.y, center.y + p.x}; + *d_first++ = point_t{center.x + p.y, center.y - p.x}; + *d_first++ = point_t{center.x - p.y, center.y + p.x}; + *d_first++ = point_t{center.x - p.y, center.y - p.x}; }; - const std::ptrdiff_t iteration_count = point_count(radius) / 8; + const std::ptrdiff_t iteration_count = point_count() / 8; double angle = 0; // do note that + 1 was done inside count estimation, thus <= is not needed, only < for (std::ptrdiff_t i = 0; i < iteration_count; ++i, angle += minimum_angle_step) @@ -74,6 +88,9 @@ struct trigonometric_circle_rasterizer translate_mirror_points({x, y}); } } + + point_t center; + std::ptrdiff_t radius; }; /// \ingroup CircleRasterization @@ -84,8 +101,18 @@ struct trigonometric_circle_rasterizer /// https://en.wikipedia.org/wiki/Midpoint_circle_algorithm struct midpoint_circle_rasterizer { + using type = circle_rasterizer_t; + + /// \brief Creates a midpoint circle rasterizer + /// \param center_point - Point containing positive integer x co-ordinate and y co-ordinate of the + /// center respectively. + /// \param circle_radius - Radius of the circle + midpoint_circle_rasterizer(point_t center_point, std::ptrdiff_t circle_radius) + : center(center_point), radius(circle_radius) + {} + /// \brief Calculate the amount of points that rasterizer will output - std::ptrdiff_t point_count(std::ptrdiff_t radius) const noexcept + std::ptrdiff_t point_count() const noexcept { // the reason for pulling 8 out is so that when the expression radius * cos(45 degrees) // is used, it would yield the same result as here @@ -95,20 +122,20 @@ struct midpoint_circle_rasterizer } /// \brief perform rasterization and output into d_first - template - void operator()(std::ptrdiff_t radius, point_t offset, RAIterator d_first) const + template + void operator()(OutputIterator d_first) const { - auto translate_mirror_points = [&d_first, offset](point_t p) { - *d_first++ = point_t{offset.x + p.x, offset.y + p.y}; - *d_first++ = point_t{offset.x + p.x, offset.y - p.y}; - *d_first++ = point_t{offset.x - p.x, offset.y + p.y}; - *d_first++ = point_t{offset.x - p.x, offset.y - p.y}; - *d_first++ = point_t{offset.x + p.y, offset.y + p.x}; - *d_first++ = point_t{offset.x + p.y, offset.y - p.x}; - *d_first++ = point_t{offset.x - p.y, offset.y + p.x}; - *d_first++ = point_t{offset.x - p.y, offset.y - p.x}; + auto translate_mirror_points = [this, &d_first](point_t p) { + *d_first++ = point_t{center.x + p.x, center.y + p.y}; + *d_first++ = point_t{center.x + p.x, center.y - p.y}; + *d_first++ = point_t{center.x - p.x, center.y + p.y}; + *d_first++ = point_t{center.x - p.x, center.y - p.y}; + *d_first++ = point_t{center.x + p.y, center.y + p.x}; + *d_first++ = point_t{center.x + p.y, center.y - p.x}; + *d_first++ = point_t{center.x - p.y, center.y + p.x}; + *d_first++ = point_t{center.x - p.y, center.y - p.x}; }; - std::ptrdiff_t iteration_distance = point_count(radius) / 8; + std::ptrdiff_t iteration_distance = point_count() / 8; std::ptrdiff_t y_current = radius; std::ptrdiff_t r_squared = radius * radius; translate_mirror_points({0, y_current}); @@ -122,8 +149,31 @@ struct midpoint_circle_rasterizer translate_mirror_points({x, y_current}); } } + + point_t center; + std::ptrdiff_t radius; }; +namespace detail { + +template +struct apply_rasterizer_op +{ + void operator()( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel) + { + std::vector trajectory(rasterizer.point_count()); + rasterizer(std::begin(trajectory)); + + for (auto const& point : trajectory) + { + view(point) = pixel; + } + } +}; + +} //namespace detail + }} // namespace boost::gil #endif diff --git a/include/boost/gil/extension/rasterization/ellipse.hpp b/include/boost/gil/extension/rasterization/ellipse.hpp index 2f9aca9442..091189c788 100644 --- a/include/boost/gil/extension/rasterization/ellipse.hpp +++ b/include/boost/gil/extension/rasterization/ellipse.hpp @@ -9,6 +9,7 @@ #define BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP #include +#include #include #include @@ -17,6 +18,8 @@ namespace boost { namespace gil { +struct ellipse_rasterizer_t{}; + /// \defgroup EllipseRasterization /// \ingroup Rasterization /// \brief Ellipse rasterization algorithms. @@ -27,11 +30,22 @@ namespace boost { namespace gil { /// it shifts origin to provided co-ordinates of center and then draws the curve. struct midpoint_ellipse_rasterizer { + using type = ellipse_rasterizer_t; + + /// \brief Creates a midpoint ellipse rasterizer + /// \param center - Point containing positive integer x co-ordinate and y co-ordinate of the + /// center respectively. + /// \param semi_axes - Point containing positive integer lengths of horizontal semi-axis + /// and vertical semi-axis respectively. + midpoint_ellipse_rasterizer(point center_point, + point semi_axes_values) + : center(center_point) + , semi_axes(semi_axes_values) + {} + /// \brief Returns a vector containing co-ordinates of first quadrant points which lie on /// rasterizer trajectory of the ellipse. - /// \param semi_axes - Point containing half of lengths of horizontal and vertical axis - /// respectively. - auto obtain_trajectory(point semi_axes) + auto obtain_trajectory() const -> std::vector { // Citation : J. Van Aken, "An Efficient Ellipse-Drawing Algorithm" in IEEE Computer @@ -100,15 +114,13 @@ struct midpoint_ellipse_rasterizer /// center with slope -1 using colours provided by user. /// \param view - Gil view of image on which the elliptical curve is to be drawn. /// \param pixel - Pixel value for the elliptical curve to be drawn. - /// \param center - Point specifying co-ordinates of center of ellipse to be drawn. /// \param trajectory_points - Constant vector specifying pixel co-ordinates of points lying /// on rasterizer trajectory. /// \tparam View - Type of input image view. /// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view template void draw_curve(View& view, Pixel const& pixel, - point center, - std::vector const& trajectory_points) + std::vector const& trajectory_points) const { using pixel_t = typename View::value_type; if (!pixels_are_compatible()) @@ -117,11 +129,13 @@ struct midpoint_ellipse_rasterizer "type of the provided pixel."); } - --center[0], --center[1]; // For converting center co-ordinate values to zero based indexing. + // mutable center copy + point center2(center); + --center2[0], --center2[1]; // For converting center co-ordinate values to zero based indexing. for (point_t pnt : trajectory_points) { - std::array co_ords = {center[0] + pnt[0], - center[0] - pnt[0], center[1] + pnt[1], center[1] - pnt[1] + std::array co_ords = {center2[0] + pnt[0], + center2[0] - pnt[0], center2[1] + pnt[1], center2[1] - pnt[1] }; bool validity[4]{}; if (co_ords[0] < view.width()) @@ -164,20 +178,32 @@ struct midpoint_ellipse_rasterizer /// in the function 'draw_curve' for drawing the desired ellipse. /// \param view - Gil view of image on which the elliptical curve is to be drawn. /// \param pixel - Pixel value for the elliptical curve to be drawn. - /// \param center - Point containing positive integer x co-ordinate and y co-ordinate of the - /// center respectively. - /// \param semi_axes - Point containing positive integer lengths of horizontal semi-axis - /// and vertical semi-axis respectively. /// \tparam View - Type of input image view. /// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view template - void operator()(View& view, Pixel const& pixel, - point center, point semi_axes) + void operator()(View& view, Pixel const& pixel) const { - draw_curve(view, pixel, center, obtain_trajectory(semi_axes)); + draw_curve(view, obtain_trajectory()); } + + point center; + point semi_axes; }; +namespace detail { + +template +struct apply_rasterizer_op +{ + void operator()( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel) + { + rasterizer(view, pixel); + } +}; + +} //namespace detail + }} // namespace boost::gil #endif diff --git a/include/boost/gil/extension/rasterization/line.hpp b/include/boost/gil/extension/rasterization/line.hpp index 53dd842540..3014cabcc7 100644 --- a/include/boost/gil/extension/rasterization/line.hpp +++ b/include/boost/gil/extension/rasterization/line.hpp @@ -8,13 +8,18 @@ #ifndef BOOST_GIL_EXTENSION_RASTERIZATION_LINE_HPP #define BOOST_GIL_EXTENSION_RASTERIZATION_LINE_HPP +#include #include #include #include +#include +#include namespace boost { namespace gil { +struct line_rasterizer_t{}; + /// \defgroup Rasterization /// \brief A set of functions to rasterize shapes /// @@ -40,21 +45,26 @@ namespace boost { namespace gil { /// https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#:~:text=Bresenham's%20line%20algorithm%20is%20a,straight%20line%20between%20two%20points. struct bresenham_line_rasterizer { - constexpr std::ptrdiff_t point_count(std::ptrdiff_t width, std::ptrdiff_t height) const noexcept - { - return width > height ? width : height; - } + using type = line_rasterizer_t; + + bresenham_line_rasterizer(point_t start, point_t end) + : start_point(start), end_point(end) + {} - std::ptrdiff_t point_count(point_t start, point_t end) const noexcept + std::ptrdiff_t point_count() const noexcept { - const auto abs_width = std::abs(end.x - start.x) + 1; - const auto abs_height = std::abs(end.y - start.y) + 1; - return point_count(abs_width, abs_height); + const auto abs_width = std::abs(end_point.x - start_point.x) + 1; + const auto abs_height = std::abs(end_point.y - start_point.y) + 1; + return abs_width > abs_height ? abs_width : abs_height; } - template - void operator()(point_t start, point_t end, RandomAccessIterator d_first) const + template + void operator()(OutputIterator d_first) const { + // mutable stack copies + point_t start = start_point; + point_t end = end_point; + if (start == end) { // put the point and immediately exit, as later on division by zero will @@ -92,8 +102,31 @@ struct bresenham_line_rasterizer } *d_first++ = needs_flip ? point_t{end.y, end.x} : end; } + + point_t start_point; + point_t end_point; +}; + +namespace detail { + +template +struct apply_rasterizer_op +{ + void operator()( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel) + { + std::vector trajectory(rasterizer.point_count()); + rasterizer(std::begin(trajectory)); + + for (auto const& point : trajectory) + { + view(point) = pixel; + } + } }; +} //namespace detail + }} // namespace boost::gil #endif diff --git a/test/extension/image_processing/hough_circle_transform.cpp b/test/extension/image_processing/hough_circle_transform.cpp index 67156e51a1..8ec4eb9f31 100644 --- a/test/extension/image_processing/hough_circle_transform.cpp +++ b/test/extension/image_processing/hough_circle_transform.cpp @@ -23,8 +23,8 @@ namespace gil = boost::gil; template void exact_fit_test(std::ptrdiff_t radius, gil::point_t offset, Rasterizer rasterizer) { - std::vector circle_points(rasterizer.point_count(radius)); - rasterizer(radius, offset, circle_points.begin()); + std::vector circle_points(rasterizer.point_count()); + rasterizer(circle_points.begin()); // const std::ptrdiff_t diameter = radius * 2 - 1; const std::ptrdiff_t width = offset.x + radius + 1; const std::ptrdiff_t height = offset.y + radius + 1; @@ -54,12 +54,12 @@ void exact_fit_test(std::ptrdiff_t radius, gil::point_t offset, Rasterizer raste }); gil::hough_circle_transform_brute(input, radius_parameter, x_parameter, y_parameter, output_views.begin(), rasterizer); - if (output_views[0](0, 0) != rasterizer.point_count(radius)) + if (output_views[0](0, 0) != rasterizer.point_count()) { std::cout << "accumulated value: " << static_cast(output_views[0](0, 0)) - << " expected value: " << rasterizer.point_count(radius) << "\n\n"; + << " expected value: " << rasterizer.point_count() << "\n\n"; } - BOOST_TEST(output_views[0](0, 0) == rasterizer.point_count(radius)); + BOOST_TEST(output_views[0](0, 0) == rasterizer.point_count()); } int main() @@ -72,9 +72,10 @@ int main() for (std::ptrdiff_t y_offset = radius; y_offset < radius + test_dim_length; ++y_offset) { - exact_fit_test(radius, {x_offset, y_offset}, gil::midpoint_circle_rasterizer{}); exact_fit_test(radius, {x_offset, y_offset}, - gil::trigonometric_circle_rasterizer{}); + gil::midpoint_circle_rasterizer{{x_offset, y_offset}, radius}); + exact_fit_test(radius, {x_offset, y_offset}, + gil::trigonometric_circle_rasterizer{{x_offset, y_offset}, radius}); } } } diff --git a/test/extension/image_processing/hough_line_transform.cpp b/test/extension/image_processing/hough_line_transform.cpp index 3be4194261..6ff4059654 100644 --- a/test/extension/image_processing/hough_line_transform.cpp +++ b/test/extension/image_processing/hough_line_transform.cpp @@ -36,11 +36,12 @@ void translate(std::vector& points, std::ptrdiff_t intercept) void hough_line_test(std::ptrdiff_t height, std::ptrdiff_t intercept) { - const auto rasterizer = gil::bresenham_line_rasterizer{}; gil::gray8_image_t image(width, width, gil::gray8_pixel_t(0)); auto input = gil::view(image); - std::vector line_points(rasterizer.point_count(width, height)); - rasterizer({0, 0}, {width - 1, height - 1}, line_points.begin()); + + const auto rasterizer = gil::bresenham_line_rasterizer{{0, 0}, {width - 1, height - 1}}; + std::vector line_points(rasterizer.point_count()); + rasterizer(line_points.begin()); translate(line_points, intercept); for (const auto& p : line_points) { diff --git a/test/extension/rasterization/circle.cpp b/test/extension/rasterization/circle.cpp index 33af44c491..4df90adf4c 100644 --- a/test/extension/rasterization/circle.cpp +++ b/test/extension/rasterization/circle.cpp @@ -15,13 +15,13 @@ namespace gil = boost::gil; template -void test_rasterizer_follows_equation(std::ptrdiff_t radius, Rasterizer rasterizer) +void test_rasterizer_follows_equation(Rasterizer rasterizer) { - - std::vector circle_points(rasterizer.point_count(radius)); + const std::ptrdiff_t radius = rasterizer.radius; + std::vector circle_points(rasterizer.point_count()); std::ptrdiff_t const r_squared = radius * radius; - rasterizer(radius, {0, 0}, circle_points.begin()); - std::vector first_octant(rasterizer.point_count(radius) / 8); + rasterizer(circle_points.begin()); + std::vector first_octant(rasterizer.point_count() / 8); for (std::size_t i = 0, octant_index = 0; i < circle_points.size(); i += 8, ++octant_index) { @@ -38,10 +38,10 @@ void test_rasterizer_follows_equation(std::ptrdiff_t radius, Rasterizer rasteriz } template -void test_connectivity(std::ptrdiff_t radius, Rasterizer rasterizer) +void test_connectivity(Rasterizer rasterizer) { - std::vector circle_points(rasterizer.point_count(radius)); - rasterizer(radius, {radius, radius}, circle_points.begin()); + std::vector circle_points(rasterizer.point_count()); + rasterizer(circle_points.begin()); for (std::size_t i = 0; i < 8; ++i) { std::vector octant(circle_points.size() / 8); @@ -65,11 +65,11 @@ int main() { for (std::ptrdiff_t radius = 5; radius <= 512; ++radius) { - test_rasterizer_follows_equation(radius, gil::midpoint_circle_rasterizer{}); + test_rasterizer_follows_equation(gil::midpoint_circle_rasterizer{{0, 0}, radius}); // TODO: find out a new testing procedure for trigonometric rasterizer // test_equation_following(radius, gil::trigonometric_circle_rasterizer{}); - test_connectivity(radius, gil::midpoint_circle_rasterizer{}); - test_connectivity(radius, gil::trigonometric_circle_rasterizer{}); + test_connectivity(gil::midpoint_circle_rasterizer{{radius, radius}, radius}); + test_connectivity(gil::trigonometric_circle_rasterizer{{radius, radius}, radius}); } return boost::report_errors(); diff --git a/test/extension/rasterization/ellipse.cpp b/test/extension/rasterization/ellipse.cpp index 1b44707917..974ca7f87e 100644 --- a/test/extension/rasterization/ellipse.cpp +++ b/test/extension/rasterization/ellipse.cpp @@ -72,9 +72,9 @@ int main() { for (float b = 1; b < 101; ++b) { - auto rasterizer = gil::midpoint_ellipse_rasterizer{}; - std::vector points = rasterizer.obtain_trajectory( - {static_cast(a), static_cast(b)}); + auto rasterizer = gil::midpoint_ellipse_rasterizer{{}, + {static_cast(a), static_cast(b)}}; + std::vector points = rasterizer.obtain_trajectory(); test_rasterizer_follows_equation(points, a, b); test_connectivity(points); } diff --git a/test/extension/rasterization/line.cpp b/test/extension/rasterization/line.cpp index 92986edbf4..f68f904f9a 100644 --- a/test/extension/rasterization/line.cpp +++ b/test/extension/rasterization/line.cpp @@ -49,9 +49,9 @@ endpoints create_endpoints(std::mt19937& twister, line_type create_line(endpoints points) { - gil::bresenham_line_rasterizer rasterizer; - line_type forward_line(rasterizer.point_count(points.start, points.end)); - rasterizer(points.start, points.end, forward_line.begin()); + gil::bresenham_line_rasterizer rasterizer(points.start, points.end); + line_type forward_line(rasterizer.point_count()); + rasterizer(forward_line.begin()); return forward_line; } From 95679b6280b4cc8a20c208fb0008c4579a1a8070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 27 Jun 2022 08:56:12 +0100 Subject: [PATCH 138/193] docs!: Announce plan to require C++17 after Boost 1.80 (#694) For Boost 1.80 we have announced switch from C++11 to C++14 as an intermediate step: https://github.com/boostorg/gil/pull/677 As we will be moving forward with refactoring and modernisations, we are planning to switch to C++17 in (near) future Boost release: https://github.com/boostorg/gil/discussions/676 --- RELEASES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 419ca6e145..ca2574ae64 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [1.80.0] - 2022-08-10 +NOTICE: We are planning BREAKING switch to C++17 as minimum required C++ language version in one or two releases after Boost 1.80 ([Discussion #676](https://github.com/boostorg/gil/discussions/676)) + ### Added - Added `image` constructor from compatible view ([PR #520](https://github.com/boostorg/gil/pull/520)) - Added inverse function for affine `matrix3x2` ([PR #527](https://github.com/boostorg/gil/pull/527)) From 103f4d31ba76ac1817867364e7e8532d73e949db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 27 Jun 2022 15:19:07 +0200 Subject: [PATCH 139/193] build: Update CMAKE_CXX_STANDARD from 11 to 14 Follow-up to PR #677 and Issue #696 --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aee245bbfe..9e5029ac46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ target_link_libraries(boost_gil Boost::variant2 ) -target_compile_features(boost_gil INTERFACE cxx_std_11) +target_compile_features(boost_gil INTERFACE cxx_std_14) else() @@ -60,14 +60,14 @@ option(BOOST_GIL_ENABLE_EXT_RASTERIZATION "Enable Rasterization extension and te option(BOOST_GIL_ENABLE_EXT_IMAGE_PROCESSING "Enable Image Processing extension (!) and tests" ON) option(BOOST_GIL_USE_CONAN "Use Conan to install dependencies" OFF) option(BOOST_GIL_USE_CLANG_TIDY "Set CMAKE_CXX_CLANG_TIDY property on targets to enable clang-tidy linting" OFF) -set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard version to use (default is 11)") +set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ standard version to use (default is 14)") #----------------------------------------------------------------------------- # Project #----------------------------------------------------------------------------- project(Boost.GIL LANGUAGES CXX - DESCRIPTION "Boost.GIL - Generic Image Library | Requires C++11 since Boost 1.68") + DESCRIPTION "Boost.GIL - Generic Image Library | Requires C++14 since Boost 1.80") list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_BINARY_DIR}/cmake) From 156dd29f282aad687d958f927f2015f2e1b8b0e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 27 Jun 2022 15:31:47 +0200 Subject: [PATCH 140/193] build: Bump Boost required by CMake from 1.72 to 1.80 From CONTRIBUTING.md: WARNING: The CMake configuration is only provided for convenience of contributors. It does not export or install any targets, deploy config files or support subproject workflow. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e5029ac46..1af6bf85c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,7 +140,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(Boost_USE_STATIC_RUNTIME OFF) endif() -find_package(Boost 1.72.0 REQUIRED COMPONENTS filesystem) +find_package(Boost 1.80.0 REQUIRED COMPONENTS filesystem) message(STATUS "Boost.GIL: Using Boost_INCLUDE_DIRS=${Boost_INCLUDE_DIRS}") message(STATUS "Boost.GIL: Using Boost_LIBRARY_DIRS=${Boost_LIBRARY_DIRS}") From fe63aa2a108e266d197de4e78f12535a8af6ce49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 27 Jun 2022 17:01:49 +0100 Subject: [PATCH 141/193] refactor: Switch to trailing return types (#599) - Trailing return types everywhere - Optionally, return type deduction where sensible (simple and short functions) This is related to introduction of common .clang-format, see https://github.com/boostorg/gil/pull/596#issuecomment-822681523 --- include/boost/gil/algorithm.hpp | 32 ++-- .../boost/gil/bit_aligned_pixel_iterator.hpp | 34 ++-- .../boost/gil/bit_aligned_pixel_reference.hpp | 33 ++-- include/boost/gil/channel.hpp | 159 ++++++++++++------ include/boost/gil/channel_algorithm.hpp | 85 ++++++---- include/boost/gil/cmyk.hpp | 6 +- include/boost/gil/color_base.hpp | 10 +- include/boost/gil/color_base_algorithm.hpp | 75 ++++++--- include/boost/gil/color_convert.hpp | 9 +- include/boost/gil/concepts/image.hpp | 2 +- include/boost/gil/concepts/image_view.hpp | 12 +- include/boost/gil/detail/math.hpp | 2 +- .../gil/extension/dynamic_image/any_image.hpp | 2 +- .../image_processing/hough_parameter.hpp | 4 +- .../image_processing/hough_transform.hpp | 18 +- .../io/bmp/detail/reader_backend.hpp | 2 +- include/boost/gil/extension/io/bmp/tags.hpp | 4 +- .../io/jpeg/detail/reader_backend.hpp | 2 +- include/boost/gil/extension/io/jpeg/tags.hpp | 4 +- .../io/png/detail/reader_backend.hpp | 2 +- include/boost/gil/extension/io/png/tags.hpp | 8 +- .../io/pnm/detail/reader_backend.hpp | 2 +- include/boost/gil/extension/io/pnm/tags.hpp | 4 +- .../io/raw/detail/reader_backend.hpp | 2 +- include/boost/gil/extension/io/raw/tags.hpp | 4 +- .../io/targa/detail/reader_backend.hpp | 2 +- include/boost/gil/extension/io/targa/tags.hpp | 4 +- .../io/tiff/detail/reader_backend.hpp | 2 +- include/boost/gil/extension/io/tiff/tags.hpp | 4 +- .../toolbox/image_types/indexed_image.hpp | 8 +- .../toolbox/image_types/subchroma_image.hpp | 14 +- include/boost/gil/histogram.hpp | 15 +- include/boost/gil/image.hpp | 31 ++-- .../adaptive_histogram_equalization.hpp | 3 +- include/boost/gil/image_processing/filter.hpp | 3 - .../histogram_equalization.hpp | 8 +- .../image_processing/histogram_matching.hpp | 7 +- .../boost/gil/image_processing/morphology.hpp | 9 +- .../boost/gil/image_processing/numeric.hpp | 22 ++- include/boost/gil/image_view_factory.hpp | 115 ++++++++----- include/boost/gil/io/base.hpp | 8 +- include/boost/gil/io/device.hpp | 16 +- .../gil/io/make_dynamic_image_writer.hpp | 51 ++---- include/boost/gil/io/make_reader.hpp | 66 ++------ include/boost/gil/io/make_scanline_reader.hpp | 24 +-- include/boost/gil/io/make_writer.hpp | 48 ++---- include/boost/gil/io/path_spec.hpp | 14 +- include/boost/gil/io/reader_base.hpp | 4 +- include/boost/gil/locator.hpp | 10 +- include/boost/gil/pixel_iterator_adaptor.hpp | 29 ++-- include/boost/gil/planar_pixel_iterator.hpp | 19 ++- include/boost/gil/point.hpp | 14 +- include/boost/gil/position_iterator.hpp | 34 ++-- include/boost/gil/premultiply.hpp | 7 +- include/boost/gil/rgb.hpp | 3 +- include/boost/gil/rgba.hpp | 3 +- include/boost/gil/step_iterator.hpp | 56 +++--- include/boost/gil/utilities.hpp | 25 +-- 58 files changed, 650 insertions(+), 545 deletions(-) diff --git a/include/boost/gil/algorithm.hpp b/include/boost/gil/algorithm.hpp index e6bf78005e..5d2bb3a43d 100644 --- a/include/boost/gil/algorithm.hpp +++ b/include/boost/gil/algorithm.hpp @@ -89,22 +89,22 @@ struct binary_operation_obj using result_type = Result; template BOOST_FORCEINLINE - result_type operator()(const std::pair& p) const { + auto operator()(const std::pair& p) const -> result_type { return apply(*p.first, *p.second, typename views_are_compatible::type()); } template BOOST_FORCEINLINE - result_type operator()(const V1& v1, const V2& v2) const { + auto operator()(const V1& v1, const V2& v2) const -> result_type { return apply(v1, v2, typename views_are_compatible::type()); } - result_type operator()(const error_t&) const { throw std::bad_cast(); } + auto operator()(const error_t&) const -> result_type { throw std::bad_cast(); } private: // dispatch from apply overload to a function with distinct name template BOOST_FORCEINLINE - result_type apply(V1 const& v1, V2 const& v2, std::false_type) const + auto apply(V1 const& v1, V2 const& v2, std::false_type) const -> result_type { return ((const Derived*)this)->apply_incompatible(v1, v2); } @@ -112,7 +112,7 @@ struct binary_operation_obj // dispatch from apply overload to a function with distinct name template BOOST_FORCEINLINE - result_type apply(V1 const& v1, V2 const& v2, std::true_type) const + auto apply(V1 const& v1, V2 const& v2, std::true_type) const -> result_type { return ((const Derived*)this)->apply_compatible(v1, v2); } @@ -120,7 +120,7 @@ struct binary_operation_obj // function with distinct name - it can be overloaded by subclasses template BOOST_FORCEINLINE - result_type apply_incompatible(V1 const& /*v1*/, V2 const& /*v2*/) const + auto apply_incompatible(V1 const& /*v1*/, V2 const& /*v2*/) const -> result_type { throw std::bad_cast(); } @@ -155,9 +155,10 @@ auto copy( /// \ingroup STLOptimizations /// \brief Copy when both src and dst are interleaved and of the same type can be just memmove template -BOOST_FORCEINLINE boost::gil::pixel* -copy(const boost::gil::pixel* first, const boost::gil::pixel* last, - boost::gil::pixel* dst) { +BOOST_FORCEINLINE +auto copy(const boost::gil::pixel* first, const boost::gil::pixel* last, + boost::gil::pixel* dst) -> boost::gil::pixel* +{ return (boost::gil::pixel*)std::copy((unsigned char*)first,(unsigned char*)last, (unsigned char*)dst); } } // namespace std @@ -174,7 +175,8 @@ namespace std { /// \ingroup STLOptimizations /// \brief Copy when both src and dst are planar pointers is copy for each channel template BOOST_FORCEINLINE -boost::gil::planar_pixel_iterator copy(boost::gil::planar_pixel_iterator first, boost::gil::planar_pixel_iterator last, boost::gil::planar_pixel_iterator dst) { +auto copy(boost::gil::planar_pixel_iterator first, boost::gil::planar_pixel_iterator last, boost::gil::planar_pixel_iterator dst) -> boost::gil::planar_pixel_iterator +{ boost::gil::gil_function_requires::value_type,typename std::iterator_traits::value_type>>(); static_for_each(first,last,dst,boost::gil::detail::copy_fn()); return dst+(last-first); @@ -250,7 +252,7 @@ struct copier_n,iterator_from_2d

    > { }; template -BOOST_FORCEINLINE DstIterator copy_with_2d_iterators(SrcIterator first, SrcIterator last, DstIterator dst) { +BOOST_FORCEINLINE auto copy_with_2d_iterators(SrcIterator first, SrcIterator last, DstIterator dst) -> DstIterator { using src_x_iterator = typename SrcIterator::x_iterator; using dst_x_iterator = typename DstIterator::x_iterator; @@ -276,9 +278,11 @@ namespace std { /// \ingroup STLOptimizations /// \brief std::copy(I1,I1,I2) with I1 and I2 being a iterator_from_2d template -BOOST_FORCEINLINE boost::gil::iterator_from_2d
      copy1(boost::gil::iterator_from_2d first, boost::gil::iterator_from_2d last, boost::gil::iterator_from_2d
        dst) { +BOOST_FORCEINLINE auto copy1(boost::gil::iterator_from_2d first, boost::gil::iterator_from_2d last, boost::gil::iterator_from_2d
          dst) -> boost::gil::iterator_from_2d
            +{ return boost::gil::detail::copy_with_2d_iterators(first,last,dst); } + } // namespace std namespace boost { namespace gil { @@ -313,13 +317,13 @@ class copy_and_convert_pixels_fn : public binary_operation_obj BOOST_FORCEINLINE - result_type apply_incompatible(const V1& src, const V2& dst) const { + auto apply_incompatible(const V1& src, const V2& dst) const -> result_type { copy_pixels(color_converted_view(src,_cc),dst); } // If the two color spaces are compatible, copy_and_convert is just copy template BOOST_FORCEINLINE - result_type apply_compatible(const V1& src, const V2& dst) const { + auto apply_compatible(const V1& src, const V2& dst) const -> result_type { copy_pixels(src,dst); } }; diff --git a/include/boost/gil/bit_aligned_pixel_iterator.hpp b/include/boost/gil/bit_aligned_pixel_iterator.hpp index e65a6a92ee..7d3059b66e 100644 --- a/include/boost/gil/bit_aligned_pixel_iterator.hpp +++ b/include/boost/gil/bit_aligned_pixel_iterator.hpp @@ -63,22 +63,22 @@ struct bit_aligned_pixel_iterator : public iterator_facade reference { bit_aligned_pixel_iterator it=*this; it.advance(d); return *it; } - reference operator->() const { return **this; } - const bit_range_t& bit_range() const { return _bit_range; } - bit_range_t& bit_range() { return _bit_range; } + auto operator->() const -> reference { return **this; } + auto bit_range() const -> bit_range_t const& { return _bit_range; } + auto bit_range() -> bit_range_t& { return _bit_range; } private: bit_range_t _bit_range; static constexpr int bit_size = NonAlignedPixelReference::bit_size; friend class boost::iterator_core_access; - reference dereference() const { return NonAlignedPixelReference(_bit_range); } + auto dereference() const -> reference { return NonAlignedPixelReference(_bit_range); } void increment() { ++_bit_range; } void decrement() { --_bit_range; } void advance(difference_type d) { _bit_range.bit_advance(d*bit_size); } - difference_type distance_to(const bit_aligned_pixel_iterator& it) const { return _bit_range.bit_distance_to(it._bit_range) / bit_size; } + auto distance_to(bit_aligned_pixel_iterator const& it) const -> difference_type { return _bit_range.bit_distance_to(it._bit_range) / bit_size; } bool equal(const bit_aligned_pixel_iterator& it) const { return _bit_range==it._bit_range; } }; @@ -122,12 +122,14 @@ struct byte_to_memunit> {}; template -inline std::ptrdiff_t memunit_step(const bit_aligned_pixel_iterator&) { +inline auto memunit_step(const bit_aligned_pixel_iterator&) -> std::ptrdiff_t +{ return NonAlignedPixelReference::bit_size; } template -inline std::ptrdiff_t memunit_distance(const bit_aligned_pixel_iterator& p1, const bit_aligned_pixel_iterator& p2) { +inline auto memunit_distance(bit_aligned_pixel_iterator const& p1, bit_aligned_pixel_iterator const& p2) -> std::ptrdiff_t +{ return (p2.bit_range().current_byte() - p1.bit_range().current_byte())*8 + p2.bit_range().bit_offset() - p1.bit_range().bit_offset(); } @@ -137,14 +139,15 @@ inline void memunit_advance(bit_aligned_pixel_iterator } template -inline bit_aligned_pixel_iterator memunit_advanced(const bit_aligned_pixel_iterator& p, std::ptrdiff_t diff) { +inline auto memunit_advanced(bit_aligned_pixel_iterator const& p, std::ptrdiff_t diff) -> bit_aligned_pixel_iterator { bit_aligned_pixel_iterator ret=p; memunit_advance(ret, diff); return ret; } template inline -NonAlignedPixelReference memunit_advanced_ref(bit_aligned_pixel_iterator it, std::ptrdiff_t diff) { +auto memunit_advanced_ref(bit_aligned_pixel_iterator it, std::ptrdiff_t diff) -> NonAlignedPixelReference +{ return *memunit_advanced(it,diff); } ///////////////////////////// @@ -183,11 +186,14 @@ namespace std { // It is important to provide an overload of uninitialized_copy for bit_aligned_pixel_iterator. The default STL implementation calls placement new, // which is not defined for bit_aligned_pixel_iterator. template -boost::gil::bit_aligned_pixel_iterator uninitialized_copy(boost::gil::bit_aligned_pixel_iterator first, - boost::gil::bit_aligned_pixel_iterator last, - boost::gil::bit_aligned_pixel_iterator dst) { +auto uninitialized_copy(boost::gil::bit_aligned_pixel_iterator first, + boost::gil::bit_aligned_pixel_iterator last, + boost::gil::bit_aligned_pixel_iterator dst) + -> boost::gil::bit_aligned_pixel_iterator +{ return std::copy(first,last,dst); } -} // namespace std +} // namespace std + #endif diff --git a/include/boost/gil/bit_aligned_pixel_reference.hpp b/include/boost/gil/bit_aligned_pixel_reference.hpp index 094ef4b951..3069de3d56 100644 --- a/include/boost/gil/bit_aligned_pixel_reference.hpp +++ b/include/boost/gil/bit_aligned_pixel_reference.hpp @@ -49,18 +49,18 @@ class bit_range { BOOST_ASSERT(bit_offset >= 0 && bit_offset < 8); } - bit_range(const bit_range& br) : _current_byte(br._current_byte), _bit_offset(br._bit_offset) {} + bit_range(bit_range const& br) : _current_byte(br._current_byte), _bit_offset(br._bit_offset) {} template bit_range(const bit_range& br) : _current_byte(br._current_byte), _bit_offset(br._bit_offset) {} - bit_range& operator=(const bit_range& br) { _current_byte = br._current_byte; _bit_offset=br._bit_offset; return *this; } - bool operator==(const bit_range& br) const { return _current_byte==br._current_byte && _bit_offset==br._bit_offset; } + auto operator=(bit_range const& br) -> bit_range& { _current_byte = br._current_byte; _bit_offset=br._bit_offset; return *this; } + bool operator==(bit_range const& br) const { return _current_byte==br._current_byte && _bit_offset==br._bit_offset; } - bit_range& operator++() { + auto operator++() -> bit_range& { _current_byte += (_bit_offset+RangeSize) / 8; _bit_offset = (_bit_offset+RangeSize) % 8; return *this; } - bit_range& operator--() { bit_advance(-RangeSize); return *this; } + auto operator--() -> bit_range& { bit_advance(-RangeSize); return *this; } void bit_advance(difference_type num_bits) { int new_offset = int(_bit_offset+num_bits); @@ -71,11 +71,13 @@ class bit_range { --_current_byte; } } - difference_type bit_distance_to(const bit_range& b) const { + + auto bit_distance_to(bit_range const& b) const -> difference_type + { return (b.current_byte() - current_byte())*8 + b.bit_offset()-bit_offset(); } - byte_t* current_byte() const { return _current_byte; } - int bit_offset() const { return _bit_offset; } + auto current_byte() const -> byte_t* { return _current_byte; } + auto bit_offset() const -> int { return _bit_offset; } }; /// \defgroup ColorBaseModelNonAlignedPixel bit_aligned_pixel_reference @@ -136,8 +138,10 @@ struct bit_aligned_pixel_reference bit_aligned_pixel_reference(){} bit_aligned_pixel_reference(data_ptr_t data_ptr, int bit_offset) : _bit_range(data_ptr, bit_offset) {} - explicit bit_aligned_pixel_reference(const bit_range_t& bit_range) : _bit_range(bit_range) {} - template bit_aligned_pixel_reference(const bit_aligned_pixel_reference& p) : _bit_range(p._bit_range) {} + explicit bit_aligned_pixel_reference(bit_range_t const& bit_range) : _bit_range(bit_range) {} + + template + bit_aligned_pixel_reference(bit_aligned_pixel_reference const& p) : _bit_range(p._bit_range) {} // Grayscale references can be constructed from the channel reference explicit bit_aligned_pixel_reference(typename kth_element_type::type const channel0) @@ -183,11 +187,12 @@ struct bit_aligned_pixel_reference auto operator->() const -> bit_aligned_pixel_reference const* { return this; } - bit_range_t const& bit_range() const { return _bit_range; } + auto bit_range() const -> bit_range_t const& { return _bit_range; } private: mutable bit_range_t _bit_range; - template friend struct bit_aligned_pixel_reference; + template + friend struct bit_aligned_pixel_reference; template static void check_compatible() { gil_function_requires >(); } @@ -369,7 +374,7 @@ namespace std { // Having three overloads allows us to swap between different (but compatible) models of PixelConcept template inline -void swap(const boost::gil::bit_aligned_pixel_reference x, R& y) { +void swap(boost::gil::bit_aligned_pixel_reference const x, R& y) { boost::gil::swap_proxy::value_type>(x,y); } @@ -381,7 +386,7 @@ void swap(typename boost::gil::bit_aligned_pixel_reference::value_ty template inline -void swap(const boost::gil::bit_aligned_pixel_reference x, const boost::gil::bit_aligned_pixel_reference y) { +void swap(boost::gil::bit_aligned_pixel_reference const x, const boost::gil::bit_aligned_pixel_reference y) { boost::gil::swap_proxy::value_type>(x,y); } diff --git a/include/boost/gil/channel.hpp b/include/boost/gil/channel.hpp index 39ea65eaca..eb2bde1f65 100644 --- a/include/boost/gil/channel.hpp +++ b/include/boost/gil/channel.hpp @@ -198,16 +198,32 @@ struct scoped_channel_value return *this; } - scoped_channel_value& operator++() { ++value_; return *this; } - scoped_channel_value& operator--() { --value_; return *this; } + auto operator++() -> scoped_channel_value& { ++value_; return *this; } + auto operator--() -> scoped_channel_value& { --value_; return *this; } - scoped_channel_value operator++(int) { scoped_channel_value tmp=*this; this->operator++(); return tmp; } - scoped_channel_value operator--(int) { scoped_channel_value tmp=*this; this->operator--(); return tmp; } + auto operator++(int) -> scoped_channel_value + { + scoped_channel_value tmp=*this; + this->operator++(); return tmp; + } + + auto operator--(int) -> scoped_channel_value + { + scoped_channel_value tmp=*this; + this->operator--(); return tmp; + } - template scoped_channel_value& operator+=(Scalar2 v) { value_+=v; return *this; } - template scoped_channel_value& operator-=(Scalar2 v) { value_-=v; return *this; } - template scoped_channel_value& operator*=(Scalar2 v) { value_*=v; return *this; } - template scoped_channel_value& operator/=(Scalar2 v) { value_/=v; return *this; } + template + auto operator+=(Scalar2 v) -> scoped_channel_value& { value_+=v; return *this; } + + template + auto operator-=(Scalar2 v) -> scoped_channel_value& { value_-=v; return *this; } + + template + auto operator*=(Scalar2 v) -> scoped_channel_value& { value_*=v; return *this; } + + template + auto operator/=(Scalar2 v) -> scoped_channel_value& { value_/=v; return *this; } operator BaseChannelValue() const { return value_; } private: @@ -313,7 +329,7 @@ class packed_channel_value value_ = packed_channel_value(static_cast(v)); } - static unsigned int num_bits() { return NumBits; } + static auto num_bits() -> unsigned int { return NumBits; } operator integer_t() const { return value_; } @@ -348,35 +364,69 @@ class packed_channel_reference_base data_ptr_t _data_ptr; // void* pointer to the first byte of the bit range using value_type = packed_channel_value; - using reference = const Derived; - using pointer = value_type *; - using const_pointer = const value_type *; + using reference = Derived const; + using pointer = value_type*; + using const_pointer = value_type const*; static constexpr int num_bits = NumBits; static constexpr bool is_mutable = IsMutable; - static value_type min_value() { return channel_traits::min_value(); } - static value_type max_value() { return channel_traits::max_value(); } + static auto min_value() -> value_type { return channel_traits::min_value(); } + static auto max_value() -> value_type { return channel_traits::max_value(); } using bitfield_t = BitField; using integer_t = typename value_type::integer_t; packed_channel_reference_base(data_ptr_t data_ptr) : _data_ptr(data_ptr) {} - packed_channel_reference_base(const packed_channel_reference_base& ref) : _data_ptr(ref._data_ptr) {} - const Derived& operator=(integer_t v) const { set(v); return derived(); } + packed_channel_reference_base(packed_channel_reference_base const& ref) : _data_ptr(ref._data_ptr) {} + + auto operator=(integer_t v) const -> Derived const& { set(v); return derived(); } - const Derived& operator++() const { set(get()+1); return derived(); } - const Derived& operator--() const { set(get()-1); return derived(); } + auto operator++() const -> Derived const& { set(get()+1); return derived(); } + auto operator--() const -> Derived const& { set(get()-1); return derived(); } - Derived operator++(int) const { Derived tmp=derived(); this->operator++(); return tmp; } - Derived operator--(int) const { Derived tmp=derived(); this->operator--(); return tmp; } + auto operator++(int) const -> Derived + { + Derived tmp=derived(); + this->operator++(); return tmp; + } + + auto operator--(int) const -> Derived + { + Derived tmp=derived(); + this->operator--(); + return tmp; + } - template const Derived& operator+=(Scalar2 v) const { set( static_cast( get() + v )); return derived(); } - template const Derived& operator-=(Scalar2 v) const { set( static_cast( get() - v )); return derived(); } - template const Derived& operator*=(Scalar2 v) const { set( static_cast( get() * v )); return derived(); } - template const Derived& operator/=(Scalar2 v) const { set( static_cast( get() / v )); return derived(); } + template + auto operator+=(Scalar2 v) const -> Derived const& + { + set( static_cast( get() + v )); + return derived(); + } + + template + auto operator-=(Scalar2 v) const -> Derived const& + { + set( static_cast( get() - v )); return derived(); + } + + template + auto operator*=(Scalar2 v) const -> Derived const& + { + set( static_cast( get() * v )); + return derived(); + } + + template + auto operator/=(Scalar2 v) const -> Derived const& + { + set( static_cast( get() / v )); + return derived(); + } operator integer_t() const { return get(); } - data_ptr_t operator &() const {return _data_ptr;} + auto operator&() const -> data_ptr_t {return _data_ptr;} + protected: using num_value_t = typename detail::num_value_fn::type; @@ -389,12 +439,15 @@ class packed_channel_reference_base const bitfield_t& get_data() const { return *static_cast(_data_ptr); } void set_data(const bitfield_t& val) const { *static_cast< bitfield_t*>(_data_ptr) = val; } #else - bitfield_t get_data() const { + auto get_data() const -> bitfield_t + { bitfield_t ret; static_copy_bytes()(gil_reinterpret_cast_c(_data_ptr),gil_reinterpret_cast(&ret)); return ret; } - void set_data(const bitfield_t& val) const { + + void set_data(bitfield_t const& val) const + { static_copy_bytes()(gil_reinterpret_cast_c(&val),gil_reinterpret_cast(_data_ptr)); } #endif @@ -403,8 +456,8 @@ class packed_channel_reference_base void set(integer_t value) const { // can this be done faster?? this->derived().set_unsafe(((value % num_values) + num_values) % num_values); } - integer_t get() const { return derived().get(); } - const Derived& derived() const { return static_cast(*this); } + auto get() const -> integer_t { return derived().get(); } + auto derived() const -> Derived const& { return static_cast(*this); } }; } // namespace detail @@ -468,9 +521,9 @@ class packed_channel_reference packed_channel_reference(const packed_channel_reference& ref) : parent_t(ref._data_ptr) {} packed_channel_reference(const mutable_reference& ref) : parent_t(ref._data_ptr) {} - unsigned first_bit() const { return FirstBit; } + auto first_bit() const -> unsigned int { return FirstBit; } - integer_t get() const { return integer_t((this->get_data()&channel_mask) >> FirstBit); } + auto get() const -> integer_t { return integer_t((this->get_data()&channel_mask) >> FirstBit); } }; /// \ingroup PackedChannelReferenceModel @@ -499,16 +552,17 @@ class packed_channel_reference return *this; } - const packed_channel_reference& operator=(const mutable_reference& ref) const { set_from_reference(ref.get_data()); return *this; } - const packed_channel_reference& operator=(const const_reference& ref) const { set_from_reference(ref.get_data()); return *this; } + auto operator=(mutable_reference const& ref) const -> packed_channel_reference const& { set_from_reference(ref.get_data()); return *this; } + auto operator=(const_reference const& ref) const -> packed_channel_reference const& { set_from_reference(ref.get_data()); return *this; } template - const packed_channel_reference& operator=(const packed_dynamic_channel_reference& ref) const { set_unsafe(ref.get()); return *this; } + auto operator=(packed_dynamic_channel_reference const& ref) const -> packed_channel_reference const& { set_unsafe(ref.get()); return *this; } - unsigned first_bit() const { return FirstBit; } + auto first_bit() const -> unsigned int { return FirstBit; } - integer_t get() const { return integer_t((this->get_data()&channel_mask) >> FirstBit); } + auto get() const -> integer_t { return integer_t((this->get_data()&channel_mask) >> FirstBit); } void set_unsafe(integer_t value) const { this->set_data((this->get_data() & ~channel_mask) | (( static_cast< BitField >( value )<set_data((this->get_data() & ~channel_mask) | (other_bits & channel_mask)); } }; @@ -599,13 +653,14 @@ class packed_dynamic_channel_reference using mutable_reference = packed_dynamic_channel_reference const; using integer_t = typename parent_t::integer_t; - packed_dynamic_channel_reference(const void* data_ptr, unsigned first_bit) : parent_t(data_ptr), _first_bit(first_bit) {} - packed_dynamic_channel_reference(const const_reference& ref) : parent_t(ref._data_ptr), _first_bit(ref._first_bit) {} - packed_dynamic_channel_reference(const mutable_reference& ref) : parent_t(ref._data_ptr), _first_bit(ref._first_bit) {} + packed_dynamic_channel_reference(void const* data_ptr, unsigned first_bit) : parent_t(data_ptr), _first_bit(first_bit) {} + packed_dynamic_channel_reference(const_reference const& ref) : parent_t(ref._data_ptr), _first_bit(ref._first_bit) {} + packed_dynamic_channel_reference(mutable_reference const& ref) : parent_t(ref._data_ptr), _first_bit(ref._first_bit) {} - unsigned first_bit() const { return _first_bit; } + auto first_bit() const -> unsigned int { return _first_bit; } - integer_t get() const { + auto get() const -> integer_t + { const BitField channel_mask = static_cast< integer_t >( parent_t::max_val ) <<_first_bit; return static_cast< integer_t >(( this->get_data()&channel_mask ) >> _first_bit ); } @@ -629,26 +684,30 @@ class packed_dynamic_channel_reference using integer_t = typename parent_t::integer_t; packed_dynamic_channel_reference(void* data_ptr, unsigned first_bit) : parent_t(data_ptr), _first_bit(first_bit) {} - packed_dynamic_channel_reference(const packed_dynamic_channel_reference& ref) : parent_t(ref._data_ptr), _first_bit(ref._first_bit) {} + packed_dynamic_channel_reference(packed_dynamic_channel_reference const& ref) : parent_t(ref._data_ptr), _first_bit(ref._first_bit) {} - packed_dynamic_channel_reference const& operator=(integer_t value) const + auto operator=(integer_t value) const -> packed_dynamic_channel_reference const& { BOOST_ASSERT(value <= parent_t::max_val); set_unsafe(value); return *this; } - const packed_dynamic_channel_reference& operator=(const mutable_reference& ref) const { set_unsafe(ref.get()); return *this; } - const packed_dynamic_channel_reference& operator=(const const_reference& ref) const { set_unsafe(ref.get()); return *this; } + auto operator=(mutable_reference const& ref) const -> packed_dynamic_channel_reference const& { set_unsafe(ref.get()); return *this; } + auto operator=(const_reference const& ref) const -> packed_dynamic_channel_reference const& { set_unsafe(ref.get()); return *this; } template - const packed_dynamic_channel_reference& operator=(const packed_channel_reference& ref) const - { set_unsafe(ref.get()); return *this; } + auto operator=(packed_channel_reference const& ref) const -> packed_dynamic_channel_reference const& + { + set_unsafe(ref.get()); + return *this; + } - unsigned first_bit() const { return _first_bit; } + auto first_bit() const -> unsigned int { return _first_bit; } - integer_t get() const { - const BitField channel_mask = static_cast< integer_t >( parent_t::max_val ) << _first_bit; + auto get() const -> integer_t + { + BitField const channel_mask = static_cast< integer_t >( parent_t::max_val ) << _first_bit; return static_cast< integer_t >(( this->get_data()&channel_mask ) >> _first_bit ); } diff --git a/include/boost/gil/channel_algorithm.hpp b/include/boost/gil/channel_algorithm.hpp index 848c9652ad..47496662fc 100644 --- a/include/boost/gil/channel_algorithm.hpp +++ b/include/boost/gil/channel_algorithm.hpp @@ -148,13 +148,16 @@ template DstChannelV + { return DstChannelV(channel_traits::min_value() + (src - channel_traits::min_value()) / channel_range() * channel_range()); } + private: template - static double channel_range() { + static auto channel_range() -> double + { return double(channel_traits::max_value()) - double(channel_traits::min_value()); } }; @@ -198,7 +201,8 @@ struct channel_converter_unsigned_integral // and the dst max value is divisible by the src max value template struct channel_converter_unsigned_integral_impl { - DstChannelV operator()(SrcChannelV src) const { + auto operator()(SrcChannelV src) const -> DstChannelV + { using integer_t = typename unsigned_integral_max_value::value_type; static const integer_t mul = unsigned_integral_max_value::value / unsigned_integral_max_value::value; return DstChannelV(src * mul); @@ -210,7 +214,8 @@ struct channel_converter_unsigned_integral_impl struct channel_converter_unsigned_integral_impl { - DstChannelV operator()(SrcChannelV src) const { + auto operator()(SrcChannelV src) const -> DstChannelV + { using integer_t = typename unsigned_integral_max_value::value_type; static const integer_t div = unsigned_integral_max_value::value / unsigned_integral_max_value::value; static const integer_t div2 = div/2; @@ -221,7 +226,8 @@ struct channel_converter_unsigned_integral_impl struct channel_converter_unsigned_integral_impl { - DstChannelV operator()(uintmax_t src) const { + auto operator()(uintmax_t src) const -> DstChannelV + { static const uintmax_t div = unsigned_integral_max_value::value / unsigned_integral_max_value::value; static const uintmax_t div2 = div/2; if (src > unsigned_integral_max_value::value - div2) @@ -259,7 +265,7 @@ struct channel_converter_unsigned_integral_impl struct channel_converter_unsigned_integral_nondivisible { - DstChannelV operator()(SrcChannelV src) const + auto operator()(SrcChannelV src) const -> DstChannelV { using dest_t = typename base_channel_type::type; return DstChannelV( @@ -275,7 +281,7 @@ struct channel_converter_unsigned_integral_nondivisible struct channel_converter_unsigned_integral_nondivisible { - DstChannelV operator()(SrcChannelV src) const + auto operator()(SrcChannelV src) const -> DstChannelV { static const double mul = unsigned_integral_max_value::value @@ -288,9 +294,10 @@ struct channel_converter_unsigned_integral_nondivisible -struct channel_converter_unsigned_integral_nondivisible { - DstChannelV operator()(SrcChannelV src) const { - +struct channel_converter_unsigned_integral_nondivisible +{ + auto operator()(SrcChannelV src) const -> DstChannelV + { using src_integer_t = typename detail::unsigned_integral_max_value::value_type; using dst_integer_t = typename detail::unsigned_integral_max_value::value_type; @@ -312,7 +319,7 @@ struct channel_converter_unsigned_integral_nondivisible struct channel_converter_unsigned { using argument_type = float32_t; using result_type = DstChannelV; - DstChannelV operator()(float32_t x) const + auto operator()(float32_t x) const -> DstChannelV { using dst_integer_t = typename detail::unsigned_integral_max_value::value_type; return DstChannelV( static_cast< dst_integer_t >(x*channel_traits::max_value()+0.5f )); @@ -322,13 +329,13 @@ template struct channel_converter_unsigned struct channel_converter_unsigned { using argument_type = float32_t; using result_type = SrcChannelV; - float32_t operator()(SrcChannelV x) const { return float32_t(x/float(channel_traits::max_value())); } + auto operator()(SrcChannelV x) const -> float32_t { return float32_t(x/float(channel_traits::max_value())); } }; template <> struct channel_converter_unsigned { using argument_type = float32_t; using result_type = float32_t; - float32_t operator()(float32_t x) const { return x; } + auto operator()(float32_t x) const -> float32_t { return x; } }; @@ -336,7 +343,8 @@ template <> struct channel_converter_unsigned { template <> struct channel_converter_unsigned { using argument_type = uint32_t; using result_type = float32_t; - float32_t operator()(uint32_t x) const { + auto operator()(uint32_t x) const -> float32_t + { // unfortunately without an explicit check it is possible to get a round-off error. We must ensure that max_value of uint32_t matches max_value of float32_t if (x>=channel_traits::max_value()) return channel_traits::max_value(); return float(x) / float(channel_traits::max_value()); @@ -346,7 +354,8 @@ template <> struct channel_converter_unsigned { template <> struct channel_converter_unsigned { using argument_type = float32_t; using result_type = uint32_t; - uint32_t operator()(float32_t x) const { + auto operator()(float32_t x) const -> uint32_t + { // unfortunately without an explicit check it is possible to get a round-off error. We must ensure that max_value of uint32_t matches max_value of float32_t if (x>=channel_traits::max_value()) return channel_traits::max_value(); @@ -406,7 +415,7 @@ template <> struct channel_convert_from_unsigned { using argument_type = uint8_t; using result_type = int8_t; using type = int8_t; - type operator()(uint8_t val) const { + type operator()(uint8_t val) const { return static_cast(static_cast(val) - 128); } }; @@ -437,7 +446,8 @@ template // Model ChannelValueConce struct channel_converter { using argument_type = SrcChannelV; using result_type = DstChannelV; - DstChannelV operator()(const SrcChannelV& src) const { + auto operator()(SrcChannelV const& src) const -> DstChannelV + { using to_unsigned = detail::channel_convert_to_unsigned; using from_unsigned = detail::channel_convert_from_unsigned; using converter_unsigned = channel_converter_unsigned; @@ -448,7 +458,8 @@ struct channel_converter { /// \ingroup ChannelConvertAlgorithm /// \brief Converting from one channel type to another. template // Model ChannelConcept (could be channel references) -inline typename channel_traits::value_type channel_convert(const SrcChannel& src) { +inline auto channel_convert(SrcChannel const& src) -> typename channel_traits::value_type +{ return channel_converter::value_type, typename channel_traits::value_type>()(src); } @@ -459,17 +470,26 @@ inline typename channel_traits::value_type channel_convert(const Src /// on heterogeneous pixels. struct default_channel_converter { template - void operator()(const Ch1& src, Ch2& dst) const { + void operator()(Ch1 const& src, Ch2& dst) const + { dst=channel_convert(src); } }; -namespace detail { +namespace detail +{ // fast integer division by 255 - inline uint32_t div255(uint32_t in) { uint32_t tmp=in+128; return (tmp + (tmp>>8))>>8; } + inline auto div255(uint32_t in) -> uint32_t + { + uint32_t tmp = in + 128; + return (tmp + (tmp >> 8)) >> 8; + } // fast integer divison by 32768 - inline uint32_t div32768(uint32_t in) { return (in+16384)>>15; } + inline auto div32768(uint32_t in) -> uint32_t + { + return (in + 16384) >> 15; + } } /// \defgroup ChannelMultiplyAlgorithm channel_multiply @@ -491,7 +511,8 @@ struct channel_multiplier_unsigned { using first_argument_type = ChannelValue; using second_argument_type = ChannelValue; using result_type = ChannelValue; - ChannelValue operator()(ChannelValue a, ChannelValue b) const { + auto operator()(ChannelValue a, ChannelValue b) const -> ChannelValue + { return ChannelValue(static_cast::type>(a / double(channel_traits::max_value()) * b)); } }; @@ -501,7 +522,7 @@ template<> struct channel_multiplier_unsigned { using first_argument_type = uint8_t; using second_argument_type = uint8_t; using result_type = uint8_t; - uint8_t operator()(uint8_t a, uint8_t b) const { return uint8_t(detail::div255(uint32_t(a) * uint32_t(b))); } + auto operator()(uint8_t a, uint8_t b) const -> uint8_t { return uint8_t(detail::div255(uint32_t(a) * uint32_t(b))); } }; /// \brief Specialization of channel_multiply for 16-bit unsigned channels @@ -509,7 +530,7 @@ template<> struct channel_multiplier_unsigned { using first_argument_type = uint16_t; using second_argument_type = uint16_t; using result_type = uint16_t; - uint16_t operator()(uint16_t a, uint16_t b) const { return uint16_t((uint32_t(a) * uint32_t(b))/65535); } + auto operator()(uint16_t a, uint16_t b) const -> uint16_t { return uint16_t((uint32_t(a) * uint32_t(b))/65535); } }; /// \brief Specialization of channel_multiply for float 0..1 channels @@ -517,7 +538,7 @@ template<> struct channel_multiplier_unsigned { using first_argument_type = float32_t; using second_argument_type = float32_t; using result_type = float32_t; - float32_t operator()(float32_t a, float32_t b) const { return a*b; } + auto operator()(float32_t a, float32_t b) const -> float32_t { return a*b; } }; /// \brief A function object to multiply two channels. result = a * b / max_value @@ -526,7 +547,8 @@ struct channel_multiplier { using first_argument_type = ChannelValue; using second_argument_type = ChannelValue; using result_type = ChannelValue; - ChannelValue operator()(ChannelValue a, ChannelValue b) const { + auto operator()(ChannelValue a, ChannelValue b) const -> ChannelValue + { using to_unsigned = detail::channel_convert_to_unsigned; using from_unsigned = detail::channel_convert_from_unsigned; using multiplier_unsigned = channel_multiplier_unsigned; @@ -536,7 +558,8 @@ struct channel_multiplier { /// \brief A function multiplying two channels. result = a * b / max_value template // Models ChannelConcept (could be a channel reference) -inline typename channel_traits::value_type channel_multiply(Channel a, Channel b) { +inline auto channel_multiply(Channel a, Channel b) -> typename channel_traits::value_type +{ return channel_multiplier::value_type>()(a,b); } /// @} @@ -556,8 +579,8 @@ inline typename channel_traits::value_type channel_multiply(Channel a, /// \brief Default implementation. Provide overloads for performance /// \ingroup ChannelInvertAlgorithm channel_invert template // Models ChannelConcept (could be a channel reference) -inline typename channel_traits::value_type channel_invert(Channel x) { - +inline auto channel_invert(Channel x) -> typename channel_traits::value_type +{ using base_t = typename base_channel_type::type; using promoted_t = typename promote_integral::type; promoted_t const promoted_x = x; @@ -568,6 +591,6 @@ inline typename channel_traits::value_type channel_invert(Channel x) { return inverted_x; } -} } // namespace boost::gil +}} // namespace boost::gil #endif diff --git a/include/boost/gil/cmyk.hpp b/include/boost/gil/cmyk.hpp index 7b03c199f5..50c1abfa25 100644 --- a/include/boost/gil/cmyk.hpp +++ b/include/boost/gil/cmyk.hpp @@ -40,13 +40,13 @@ using cmyk_layout_t = layout; /// \ingroup ImageViewConstructors /// \brief from raw CMYK planar data template -inline typename type_from_x_iterator >::view_t -planar_cmyk_view(std::size_t width, std::size_t height, IC c, IC m, IC y, IC k, std::ptrdiff_t rowsize_in_bytes) +inline auto planar_cmyk_view(std::size_t width, std::size_t height, IC c, IC m, IC y, IC k, std::ptrdiff_t rowsize_in_bytes) + -> typename type_from_x_iterator>::view_t { using view_t = typename type_from_x_iterator >::view_t; return view_t(width, height, typename view_t::locator(planar_pixel_iterator(c,m,y,k), rowsize_in_bytes)); } -} } // namespace gil +}} // namespace gil #endif diff --git a/include/boost/gil/color_base.hpp b/include/boost/gil/color_base.hpp index aa51f97faf..022238f71c 100644 --- a/include/boost/gil/color_base.hpp +++ b/include/boost/gil/color_base.hpp @@ -34,7 +34,7 @@ auto semantic_at_c(ColorBase& p) template -typename kth_semantic_element_const_reference_type::type semantic_at_c(const ColorBase& p); +auto semantic_at_c(const ColorBase& p) -> typename kth_semantic_element_const_reference_type::type; // Forward declare element_reference_type template struct element_reference_type; @@ -178,7 +178,7 @@ struct homogeneous_color_base { return v1_; } // Support for planar_pixel_reference operator[] - Element at_c_dynamic(std::size_t i) const + auto at_c_dynamic(std::size_t i) const -> Element { if (i == 0) return v0_; @@ -277,7 +277,7 @@ struct homogeneous_color_base { return v2_; } // Support for planar_pixel_reference operator[] - Element at_c_dynamic(std::size_t i) const + auto at_c_dynamic(std::size_t i) const -> Element { switch (i) { @@ -392,7 +392,7 @@ struct homogeneous_color_base { return v3_; } // Support for planar_pixel_reference operator[] - Element at_c_dynamic(std::size_t i) const + auto at_c_dynamic(std::size_t i) const -> Element { switch (i) { @@ -527,7 +527,7 @@ struct homogeneous_color_base } // Support for planar_pixel_reference operator[] - Element at_c_dynamic(std::size_t i) const + auto at_c_dynamic(std::size_t i) const -> Element { switch (i) { diff --git a/include/boost/gil/color_base_algorithm.hpp b/include/boost/gil/color_base_algorithm.hpp index 6746f83002..233009827b 100644 --- a/include/boost/gil/color_base_algorithm.hpp +++ b/include/boost/gil/color_base_algorithm.hpp @@ -187,14 +187,18 @@ struct color_element_const_reference_type : public kth_semantic_element_const_re /// \brief Mutable accessor to the element associated with a given color name /// \ingroup ColorBaseAlgorithmColor template -typename color_element_reference_type::type get_color(ColorBase& cb, Color=Color()) { +auto get_color(ColorBase& cb, Color=Color()) + -> typename color_element_reference_type::type +{ return color_element_reference_type::get(cb); } /// \brief Constant accessor to the element associated with a given color name /// \ingroup ColorBaseAlgorithmColor template -typename color_element_const_reference_type::type get_color(const ColorBase& cb, Color=Color()) { +auto get_color(const ColorBase& cb, Color=Color()) + -> typename color_element_const_reference_type::type +{ return color_element_const_reference_type::get(cb); } @@ -435,36 +439,63 @@ template<> struct element_recursion<0> { }; // std::min and std::max don't have the mutable overloads... -template inline const Q& mutable_min(const Q& x, const Q& y) { return x inline Q& mutable_min( Q& x, Q& y) { return x inline const Q& mutable_max(const Q& x, const Q& y) { return x inline Q& mutable_max( Q& x, Q& y) { return x +inline auto mutable_min(Q const& x, Q const& y) -> Q const& { return x +inline auto mutable_min(Q& x, Q& y) -> Q& { return x +inline auto mutable_max(Q const& x, Q const& y) -> Q const& { return x +inline auto mutable_max(Q& x, Q& y) -> Q& { return x -struct min_max_recur { - template static typename element_const_reference_type

            ::type max_(const P& p) { +struct min_max_recur +{ + template + static auto max_(P const& p) -> typename element_const_reference_type

            ::type + { return mutable_max(min_max_recur::max_(p),semantic_at_c(p)); } - template static typename element_reference_type

            ::type max_( P& p) { + + template + static auto max_(P& p) -> typename element_reference_type

            ::type + { return mutable_max(min_max_recur::max_(p),semantic_at_c(p)); } - template static typename element_const_reference_type

            ::type min_(const P& p) { + + template + static auto min_(P const& p) -> typename element_const_reference_type

            ::type + { return mutable_min(min_max_recur::min_(p),semantic_at_c(p)); } - template static typename element_reference_type

            ::type min_( P& p) { + + template + static auto min_(P& p) -> typename element_reference_type

            ::type + { return mutable_min(min_max_recur::min_(p),semantic_at_c(p)); } }; // termination condition of the compile-time recursion for min/max element template <> -struct min_max_recur<1> { - template static typename element_const_reference_type

            ::type max_(const P& p) { return semantic_at_c<0>(p); } - template static typename element_reference_type

            ::type max_( P& p) { return semantic_at_c<0>(p); } - template static typename element_const_reference_type

            ::type min_(const P& p) { return semantic_at_c<0>(p); } - template static typename element_reference_type

            ::type min_( P& p) { return semantic_at_c<0>(p); } +struct min_max_recur<1> +{ + template + static auto max_(P const& p) -> typename element_const_reference_type

            ::type { return semantic_at_c<0>(p); } + + template + static auto max_(P& p) -> typename element_reference_type

            ::type { return semantic_at_c<0>(p); } + + template + static auto min_(P const& p) -> typename element_const_reference_type

            ::type { return semantic_at_c<0>(p); } + + template + static auto min_(P& p) -> typename element_reference_type

            ::type { return semantic_at_c<0>(p); } }; } // namespace detail @@ -483,19 +514,19 @@ struct min_max_recur<1> { template BOOST_FORCEINLINE -typename element_const_reference_type

            ::type static_max(const P& p) { return detail::min_max_recur::value>::max_(p); } +auto static_max(P const& p) -> typename element_const_reference_type

            ::type { return detail::min_max_recur::value>::max_(p); } template BOOST_FORCEINLINE -typename element_reference_type

            ::type static_max( P& p) { return detail::min_max_recur::value>::max_(p); } +auto static_max(P& p) -> typename element_reference_type

            ::type { return detail::min_max_recur::value>::max_(p); } template BOOST_FORCEINLINE -typename element_const_reference_type

            ::type static_min(const P& p) { return detail::min_max_recur::value>::min_(p); } +auto static_min(P const& p) -> typename element_const_reference_type

            ::type { return detail::min_max_recur::value>::min_(p); } template BOOST_FORCEINLINE -typename element_reference_type

            ::type static_min( P& p) { return detail::min_max_recur::value>::min_(p); } +auto static_min(P& p) -> typename element_reference_type

            ::type { return detail::min_max_recur::value>::min_(p); } /// \} /// \defgroup ColorBaseAlgorithmEqual static_equal @@ -515,7 +546,7 @@ typename element_reference_type

            ::type static_min( P& p) { return d template BOOST_FORCEINLINE -bool static_equal(const P1& p1, const P2& p2) { return detail::element_recursion::value>::static_equal(p1,p2); } +bool static_equal(P1 const& p1, const P2& p2) { return detail::element_recursion::value>::static_equal(p1,p2); } /// \} @@ -708,6 +739,6 @@ BOOST_FORCEINLINE Op static_for_each(const P1& p1,const P2& p2,const P3& p3,Op op) { return detail::element_recursion::value>::static_for_each(p1,p2,p3,op); } ///\} -} } // namespace boost::gil +}} // namespace boost::gil #endif diff --git a/include/boost/gil/color_convert.hpp b/include/boost/gil/color_convert.hpp index a4b6427023..cd125e4bd3 100644 --- a/include/boost/gil/color_convert.hpp +++ b/include/boost/gil/color_convert.hpp @@ -63,7 +63,8 @@ namespace detail { // The default implementation of to_luminance uses float0..1 as the intermediate channel type template struct rgb_to_luminance_fn { - GrayChannelValue operator()(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) const { + auto operator()(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) const -> GrayChannelValue + { return channel_convert(float32_t( channel_convert(red )*0.30f + channel_convert(green)*0.59f + @@ -74,14 +75,16 @@ struct rgb_to_luminance_fn { // performance specialization for unsigned char template struct rgb_to_luminance_fn { - GrayChannelValue operator()(uint8_t red, uint8_t green, uint8_t blue) const { + auto operator()(uint8_t red, uint8_t green, uint8_t blue) const -> GrayChannelValue + { return channel_convert(uint8_t( ((uint32_t(red )*4915 + uint32_t(green)*9667 + uint32_t(blue )*1802) + 8192) >> 14)); } }; template -typename channel_traits::value_type rgb_to_luminance(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) { +auto rgb_to_luminance(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) -> typename channel_traits::value_type +{ return rgb_to_luminance_fn::value_type>()(red,green,blue); } diff --git a/include/boost/gil/concepts/image.hpp b/include/boost/gil/concepts/image.hpp index 99a7d5f53f..15b1f1a149 100644 --- a/include/boost/gil/concepts/image.hpp +++ b/include/boost/gil/concepts/image.hpp @@ -48,7 +48,7 @@ namespace boost { namespace gil { /// void Image::recreate(point_t new_dims, std::size_t alignment=1); /// void Image::recreate(point_t new_dims, value_type fill_value, std::size_t alignment); /// -/// const point_t& Image::dimensions() const; +/// point_t const& Image::dimensions() const; /// const const_view_t& const_view(const Image&); /// const view_t& view(Image&); /// }; diff --git a/include/boost/gil/concepts/image_view.hpp b/include/boost/gil/concepts/image_view.hpp index 0818b9cd35..ffcd41258e 100644 --- a/include/boost/gil/concepts/image_view.hpp +++ b/include/boost/gil/concepts/image_view.hpp @@ -89,14 +89,14 @@ namespace boost { namespace gil { /// iterator View::end() const; /// reverse_iterator View::rbegin() const; /// reverse_iterator View::rend() const; -/// iterator View::at(const point_t&); +/// iterator View::at(point_t const&); /// point_t View::dimensions() const; // number of elements along each dimension /// bool View::is_1d_traversable() const; // can an iterator over the first dimension visit each value? I.e. are there gaps between values? /// /// // iterator along a given dimension starting at a given point -/// template View::axis::iterator View::axis_iterator(const point_t&) const; +/// template View::axis::iterator View::axis_iterator(point_t const&) const; /// -/// reference operator()(View,const point_t&) const; +/// reference operator()(View,point_t const&) const; /// }; /// \endcode template @@ -196,17 +196,17 @@ struct RandomAccessNDImageViewConcept /// y_coord_t View::height() const; /// /// // X-navigation -/// x_iterator View::x_at(const point_t&) const; +/// x_iterator View::x_at(point_t const&) const; /// x_iterator View::row_begin(y_coord_t) const; /// x_iterator View::row_end (y_coord_t) const; /// /// // Y-navigation -/// y_iterator View::y_at(const point_t&) const; +/// y_iterator View::y_at(point_t const&) const; /// y_iterator View::col_begin(x_coord_t) const; /// y_iterator View::col_end (x_coord_t) const; /// /// // navigating in 2D -/// xy_locator View::xy_at(const point_t&) const; +/// xy_locator View::xy_at(point_t const&) const; /// /// // (x,y) versions of all methods taking point_t /// View::View(x_coord_t,y_coord_t,const locator&); diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index f7f45d2188..700136edcb 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -21,7 +21,7 @@ static constexpr std::array dy_sobel = {{1, 2, 1, 0, 0, 0, -1, -2, -1} static constexpr std::array dy_scharr = {{1, 1, 1, 0, 0, 0, -1, -1, -1}}; template -inline detail::kernel_2d get_identity_kernel() +inline auto get_identity_kernel() -> detail::kernel_2d { detail::kernel_2d kernel(1, 0, 0); kernel[0] = 1; diff --git a/include/boost/gil/extension/dynamic_image/any_image.hpp b/include/boost/gil/extension/dynamic_image/any_image.hpp index 590a193570..2af5164082 100644 --- a/include/boost/gil/extension/dynamic_image/any_image.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image.hpp @@ -118,7 +118,7 @@ class any_image : public variant2::variant return *this; } - void recreate(const point_t& dims, unsigned alignment=1) + void recreate(point_t const& dims, unsigned alignment=1) { variant2::visit(detail::recreate_image_fnobj(dims, alignment), *this); } diff --git a/include/boost/gil/extension/image_processing/hough_parameter.hpp b/include/boost/gil/extension/image_processing/hough_parameter.hpp index b21585d3cf..91521d1a9c 100644 --- a/include/boost/gil/extension/image_processing/hough_parameter.hpp +++ b/include/boost/gil/extension/image_processing/hough_parameter.hpp @@ -99,8 +99,8 @@ inline double minimum_angle_step(point_t dimensions) /// WARNING: do keep in mind IEEE 754 quirks, e.g. no-associativity, /// no-commutativity and precision. Do not expect expressions that are /// mathematically the same to produce the same values -inline hough_parameter make_theta_parameter(double approx_angle, double neighborhood, - point_t dimensions) +inline auto make_theta_parameter(double approx_angle, double neighborhood, point_t dimensions) + -> hough_parameter { auto angle_step = minimum_angle_step(dimensions); diff --git a/include/boost/gil/extension/image_processing/hough_transform.hpp b/include/boost/gil/extension/image_processing/hough_transform.hpp index db63fbe837..7331ed9260 100644 --- a/include/boost/gil/extension/image_processing/hough_transform.hpp +++ b/include/boost/gil/extension/image_processing/hough_transform.hpp @@ -35,9 +35,9 @@ namespace boost { namespace gil { /// accumulator array. The theta parameter is best computed through factory function /// provided in hough_parameter.hpp template -void hough_line_transform(const InputView& input_view, const OutputView& accumulator_array, - const hough_parameter& theta, - const hough_parameter& radius) +void hough_line_transform(InputView const& input_view, OutputView const& accumulator_array, + hough_parameter const& theta, + hough_parameter const& radius) { std::ptrdiff_t r_lower_bound = radius.start_point; std::ptrdiff_t r_upper_bound = r_lower_bound + radius.step_size * (radius.step_count - 1); @@ -82,11 +82,11 @@ void hough_line_transform(const InputView& input_view, const OutputView& accumul /// then is translated for every origin (x, y) in x y parameter space. For available /// circle rasterizers, please look at rasterization/circle.hpp template -void hough_circle_transform_brute(const ImageView& input, - const hough_parameter radius_parameter, - const hough_parameter x_parameter, - const hough_parameter& y_parameter, - ForwardIterator d_first, Rasterizer) +void hough_circle_transform_brute(ImageView const& input, + hough_parameter const& radius_parameter, + hough_parameter const& x_parameter, + hough_parameter const& y_parameter, + ForwardIterator d_first, Rasterizer rasterizer) { for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; ++radius_index) { @@ -97,7 +97,7 @@ void hough_circle_transform_brute(const ImageView& input, rasterizer(circle_points.begin()); // sort by scanline to improve cache coherence for row major images std::sort(circle_points.begin(), circle_points.end(), - [](const point_t& lhs, const point_t& rhs) { return lhs.y < rhs.y; }); + [](point_t const& lhs, point_t const& rhs) { return lhs.y < rhs.y; }); const auto translate = [](std::vector& points, point_t offset) { std::transform(points.begin(), points.end(), points.begin(), [offset](point_t point) { return point_t(point.x + offset.x, point.y + offset.y); diff --git a/include/boost/gil/extension/io/bmp/detail/reader_backend.hpp b/include/boost/gil/extension/io/bmp/detail/reader_backend.hpp index 5cf45f7edf..246d06ee6b 100644 --- a/include/boost/gil/extension/io/bmp/detail/reader_backend.hpp +++ b/include/boost/gil/extension/io/bmp/detail/reader_backend.hpp @@ -200,7 +200,7 @@ struct reader_backend< Device } /// Check if image is large enough. - void check_image_size( const point_t& img_dim ) + void check_image_size( point_t const& img_dim ) { if( _settings._dim.x > 0 ) { diff --git a/include/boost/gil/extension/io/bmp/tags.hpp b/include/boost/gil/extension/io/bmp/tags.hpp index 5dfcc6892c..c3ba18fb8e 100644 --- a/include/boost/gil/extension/io/bmp/tags.hpp +++ b/include/boost/gil/extension/io/bmp/tags.hpp @@ -136,8 +136,8 @@ struct image_read_settings< bmp_tag > : public image_read_settings_base /// Constructor /// \param top_left Top left coordinate for reading partial image. /// \param dim Dimensions for reading partial image. - image_read_settings( const point_t& top_left - , const point_t& dim + image_read_settings( point_t const& top_left + , point_t const& dim ) : image_read_settings_base( top_left , dim diff --git a/include/boost/gil/extension/io/jpeg/detail/reader_backend.hpp b/include/boost/gil/extension/io/jpeg/detail/reader_backend.hpp index 90e6075958..022d219f6b 100644 --- a/include/boost/gil/extension/io/jpeg/detail/reader_backend.hpp +++ b/include/boost/gil/extension/io/jpeg/detail/reader_backend.hpp @@ -189,7 +189,7 @@ struct reader_backend< Device } /// Check if image is large enough. - void check_image_size( const point_t& img_dim ) + void check_image_size( point_t const& img_dim ) { if( _settings._dim.x > 0 ) { diff --git a/include/boost/gil/extension/io/jpeg/tags.hpp b/include/boost/gil/extension/io/jpeg/tags.hpp index e0db071b25..0ebbe7cfca 100644 --- a/include/boost/gil/extension/io/jpeg/tags.hpp +++ b/include/boost/gil/extension/io/jpeg/tags.hpp @@ -150,8 +150,8 @@ struct image_read_settings< jpeg_tag > : public image_read_settings_base /// \param top_left Top left coordinate for reading partial image. /// \param dim Dimensions for reading partial image. /// \param dct_method Specifies dct method. - image_read_settings( const point_t& top_left - , const point_t& dim + image_read_settings( point_t const& top_left + , point_t const& dim , jpeg_dct_method::type dct_method = jpeg_dct_method::default_value ) : image_read_settings_base( top_left diff --git a/include/boost/gil/extension/io/png/detail/reader_backend.hpp b/include/boost/gil/extension/io/png/detail/reader_backend.hpp index aaf5843ef0..3f49070f3b 100644 --- a/include/boost/gil/extension/io/png/detail/reader_backend.hpp +++ b/include/boost/gil/extension/io/png/detail/reader_backend.hpp @@ -601,7 +601,7 @@ struct reader_backend< Device } /// Check if image is large enough. - void check_image_size( const point_t& img_dim ) + void check_image_size( point_t const& img_dim ) { if( _settings._dim.x > 0 ) { diff --git a/include/boost/gil/extension/io/png/tags.hpp b/include/boost/gil/extension/io/png/tags.hpp index 3c725be037..77ad0366fb 100644 --- a/include/boost/gil/extension/io/png/tags.hpp +++ b/include/boost/gil/extension/io/png/tags.hpp @@ -679,8 +679,8 @@ struct image_read_settings< png_tag > : public image_read_settings_base /// \param top_left Top left coordinate for reading partial image. /// \param dim Dimensions for reading partial image. /// \param gamma Screen gamma value. - image_read_settings( const point_t& top_left - , const point_t& dim + image_read_settings( point_t const& top_left + , point_t const& dim , const bool apply_screen_gamma = false , const png_gamma::type& screen_gamma = 1.0 ) @@ -715,8 +715,8 @@ struct image_read_settings< png_tag > : public image_read_settings_base , _screen_gamma ( 2 ) {} - image_read_settings( const point_t& top_left - , const point_t& dim + image_read_settings( point_t const& top_left + , point_t const& dim ) : image_read_settings_base( top_left , dim diff --git a/include/boost/gil/extension/io/pnm/detail/reader_backend.hpp b/include/boost/gil/extension/io/pnm/detail/reader_backend.hpp index 907cbd012a..76cd86f262 100644 --- a/include/boost/gil/extension/io/pnm/detail/reader_backend.hpp +++ b/include/boost/gil/extension/io/pnm/detail/reader_backend.hpp @@ -82,7 +82,7 @@ struct reader_backend< Device } /// Check if image is large enough. - void check_image_size( const point_t& img_dim ) + void check_image_size( point_t const& img_dim ) { if( _settings._dim.x > 0 ) { diff --git a/include/boost/gil/extension/io/pnm/tags.hpp b/include/boost/gil/extension/io/pnm/tags.hpp index a3ce0ff005..b2a29e72c8 100644 --- a/include/boost/gil/extension/io/pnm/tags.hpp +++ b/include/boost/gil/extension/io/pnm/tags.hpp @@ -72,8 +72,8 @@ struct image_read_settings< pnm_tag > : public image_read_settings_base /// Constructor /// \param top_left Top left coordinate for reading partial image. /// \param dim Dimensions for reading partial image. - image_read_settings( const point_t& top_left - , const point_t& dim + image_read_settings( point_t const& top_left + , point_t const& dim ) : image_read_settings_base( top_left , dim diff --git a/include/boost/gil/extension/io/raw/detail/reader_backend.hpp b/include/boost/gil/extension/io/raw/detail/reader_backend.hpp index 8fb45fe57a..cd2616dfdc 100644 --- a/include/boost/gil/extension/io/raw/detail/reader_backend.hpp +++ b/include/boost/gil/extension/io/raw/detail/reader_backend.hpp @@ -98,7 +98,7 @@ struct reader_backend< Device } /// Check if image is large enough. - void check_image_size( const point_t& img_dim ) + void check_image_size( point_t const& img_dim ) { if( _settings._dim.x > 0 ) { diff --git a/include/boost/gil/extension/io/raw/tags.hpp b/include/boost/gil/extension/io/raw/tags.hpp index e6912a6b35..f9365a7814 100644 --- a/include/boost/gil/extension/io/raw/tags.hpp +++ b/include/boost/gil/extension/io/raw/tags.hpp @@ -182,8 +182,8 @@ struct image_read_settings< raw_tag > : public image_read_settings_base /// Constructor /// \param top_left Top left coordinate for reading partial image. /// \param dim Dimensions for reading partial image. - image_read_settings( const point_t& top_left - , const point_t& dim + image_read_settings( point_t const& top_left + , point_t const& dim ) : image_read_settings_base( top_left , dim diff --git a/include/boost/gil/extension/io/targa/detail/reader_backend.hpp b/include/boost/gil/extension/io/targa/detail/reader_backend.hpp index 7990dc0d9f..3841a9a64c 100644 --- a/include/boost/gil/extension/io/targa/detail/reader_backend.hpp +++ b/include/boost/gil/extension/io/targa/detail/reader_backend.hpp @@ -122,7 +122,7 @@ struct reader_backend< Device } /// Check if image is large enough. - void check_image_size( const point_t& img_dim ) + void check_image_size( point_t const& img_dim ) { if( _settings._dim.x > 0 ) { diff --git a/include/boost/gil/extension/io/targa/tags.hpp b/include/boost/gil/extension/io/targa/tags.hpp index 525101079c..faee40461a 100644 --- a/include/boost/gil/extension/io/targa/tags.hpp +++ b/include/boost/gil/extension/io/targa/tags.hpp @@ -143,8 +143,8 @@ struct image_read_settings< targa_tag > : public image_read_settings_base /// Constructor /// \param top_left Top left coordinate for reading partial image. /// \param dim Dimensions for reading partial image. - image_read_settings( const point_t& top_left - , const point_t& dim + image_read_settings( point_t const& top_left + , point_t const& dim ) : image_read_settings_base( top_left , dim diff --git a/include/boost/gil/extension/io/tiff/detail/reader_backend.hpp b/include/boost/gil/extension/io/tiff/detail/reader_backend.hpp index 6c876a0c1f..42dc363159 100644 --- a/include/boost/gil/extension/io/tiff/detail/reader_backend.hpp +++ b/include/boost/gil/extension/io/tiff/detail/reader_backend.hpp @@ -103,7 +103,7 @@ struct reader_backend< Device } /// Check if image is large enough. - void check_image_size( const point_t& img_dim ) + void check_image_size( point_t const& img_dim ) { if( _settings._dim.x > 0 ) { diff --git a/include/boost/gil/extension/io/tiff/tags.hpp b/include/boost/gil/extension/io/tiff/tags.hpp index cae09c1afa..e4a3ebfa29 100644 --- a/include/boost/gil/extension/io/tiff/tags.hpp +++ b/include/boost/gil/extension/io/tiff/tags.hpp @@ -279,8 +279,8 @@ struct image_read_settings< tiff_tag > : public image_read_settings_base /// \param top_left Top left coordinate for reading partial image. /// \param dim Dimensions for reading partial image. /// \param directory Defines the page to read in a multipage tiff file. - image_read_settings( const point_t& top_left - , const point_t& dim + image_read_settings( point_t const& top_left + , point_t const& dim , const tiff_directory::type& directory = tiff_directory::default_value::value ) : image_read_settings_base( top_left diff --git a/include/boost/gil/extension/toolbox/image_types/indexed_image.hpp b/include/boost/gil/extension/toolbox/image_types/indexed_image.hpp index f740d93084..cb49377b8b 100644 --- a/include/boost/gil/extension/toolbox/image_types/indexed_image.hpp +++ b/include/boost/gil/extension/toolbox/image_types/indexed_image.hpp @@ -100,7 +100,7 @@ struct indexed_image_deref_fn : indexed_image_deref_fn_base< IndicesLoc ) {} - typename base_t::result_type operator()( const point_t& p ) const + typename base_t::result_type operator()( point_t const& p ) const { return * this->_palette_loc.xy_at( at_c<0>( *this->_indices_loc.xy_at( p )), 0 ); } @@ -166,7 +166,7 @@ class indexed_image_view : public image_view< Locator > , _num_colors( 0 ) {} - indexed_image_view( const point_t& dimensions + indexed_image_view( point_t const& dimensions , std::size_t num_colors , const Locator& locator ) @@ -280,7 +280,7 @@ class indexed_image init( point_t( width, height ), num_colors ); } - indexed_image( const point_t& dimensions + indexed_image( point_t const& dimensions , const std::size_t num_colors = 1 , const std::size_t indices_alignment = 0 , const std::size_t palette_alignment = 0 @@ -323,7 +323,7 @@ class indexed_image private: - void init( const point_t& dimensions + void init( point_t const& dimensions , const std::size_t num_colors ) { diff --git a/include/boost/gil/extension/toolbox/image_types/subchroma_image.hpp b/include/boost/gil/extension/toolbox/image_types/subchroma_image.hpp index cd7de24b29..493467c139 100644 --- a/include/boost/gil/extension/toolbox/image_types/subchroma_image.hpp +++ b/include/boost/gil/extension/toolbox/image_types/subchroma_image.hpp @@ -96,7 +96,7 @@ struct subchroma_image_deref_fn {} /// operator() - result_type operator()( const point_t& p ) const + result_type operator()( point_t const& p ) const { using scaling_factors_t = detail::scaling_factors < @@ -228,9 +228,9 @@ class subchroma_image_view : public image_view {} /// constructor - subchroma_image_view( const point_t& y_dimensions - , const point_t& v_dimensions - , const point_t& u_dimensions + subchroma_image_view( point_t const& y_dimensions + , point_t const& v_dimensions + , point_t const& u_dimensions , const Locator& locator ) : image_view< Locator >( y_dimensions, locator ) @@ -248,9 +248,9 @@ class subchroma_image_view : public image_view point_t v_ssfactors() const { return point_t( get_deref_fn().vx_ssfactor(), get_deref_fn().vx_ssfactor() ); } point_t u_ssfactors() const { return point_t( get_deref_fn().ux_ssfactor(), get_deref_fn().ux_ssfactor() ); } - const point_t& y_dimension() const { return _y_dimensions; } - const point_t& v_dimension() const { return _v_dimensions; } - const point_t& u_dimension() const { return _u_dimensions; } + point_t const& y_dimension() const { return _y_dimensions; } + point_t const& v_dimension() const { return _v_dimensions; } + point_t const& u_dimension() const { return _u_dimensions; } const plane_locator_t& y_plane() const { return get_deref_fn().y_locator(); } const plane_locator_t& v_plane() const { return get_deref_fn().v_locator(); } diff --git a/include/boost/gil/histogram.hpp b/include/boost/gil/histogram.hpp index 8d78caff1c..9bf66ab90b 100644 --- a/include/boost/gil/histogram.hpp +++ b/include/boost/gil/histogram.hpp @@ -45,16 +45,17 @@ namespace detail { /// \ingroup Histogram-Helpers /// template -inline typename std::enable_if::type - hash_tuple_impl(std::size_t&, std::tuple const&) +inline auto hash_tuple_impl(std::size_t&, std::tuple const&) + -> typename std::enable_if::type { + // terminating case } /// \ingroup Histogram-Helpers /// template -inline typename std::enable_if::type - hash_tuple_impl(std::size_t& seed, std::tuple const& t) +inline auto hash_tuple_impl(std::size_t& seed, std::tuple const& t) + -> typename std::enable_if::type { boost::hash_combine(seed, std::get(t)); hash_tuple_impl(seed, t); @@ -72,7 +73,7 @@ inline typename std::enable_if::type template struct hash_tuple { - std::size_t operator()(std::tuple const& t) const + auto operator()(std::tuple const& t) const -> std::size_t { std::size_t seed = 0; hash_tuple_impl<0>(seed, t); @@ -665,10 +666,10 @@ void fill_histogram( /// #bins * log( #bins ) by sorting the keys and then calculating the cumulative version. /// template -Container cumulative_histogram(Container const&); +auto cumulative_histogram(Container const&) -> Container; template -histogram cumulative_histogram(histogram const& hist) +auto cumulative_histogram(histogram const& hist) -> histogram { using check_list = boost::mp11::mp_list...>; static_assert( diff --git a/include/boost/gil/image.hpp b/include/boost/gil/image.hpp index 23763d412a..d6a321bd16 100644 --- a/include/boost/gil/image.hpp +++ b/include/boost/gil/image.hpp @@ -55,7 +55,7 @@ class image using x_coord_t = coord_t; using y_coord_t = coord_t; - const point_t& dimensions() const { return _view.dimensions(); } + point_t const& dimensions() const { return _view.dimensions(); } x_coord_t width() const { return _view.width(); } y_coord_t height() const { return _view.height(); } @@ -64,7 +64,7 @@ class image _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in), _allocated_bytes( 0 ) {} // Create with dimensions and optional initial value and alignment - image(const point_t& dimensions, + image(point_t const& dimensions, std::size_t alignment=0, const Alloc alloc_in = Alloc()) : _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in) , _allocated_bytes( 0 ) @@ -80,7 +80,7 @@ class image allocate_and_default_construct(point_t(width,height)); } - image(const point_t& dimensions, + image(point_t const& dimensions, const Pixel& p_in, std::size_t alignment = 0, const Alloc alloc_in = Alloc()) : _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in) @@ -247,7 +247,7 @@ class image ///////////////////// // without Allocator - void recreate(const point_t& dims, std::size_t alignment = 0) + void recreate(point_t const& dims, std::size_t alignment = 0) { if (dims == _view.dimensions() && _align_in_bytes == alignment) return; @@ -272,7 +272,7 @@ class image recreate(point_t(width, height), alignment); } - void recreate(const point_t& dims, const Pixel& p_in, std::size_t alignment = 0) + void recreate(point_t const& dims, const Pixel& p_in, std::size_t alignment = 0) { if (dims == _view.dimensions() && _align_in_bytes == alignment) return; @@ -298,7 +298,7 @@ class image } // with Allocator - void recreate(const point_t& dims, std::size_t alignment, const Alloc alloc_in) + void recreate(point_t const& dims, std::size_t alignment, const Alloc alloc_in) { if (dims == _view.dimensions() && _align_in_bytes == alignment && alloc_in == _alloc) return; @@ -323,7 +323,7 @@ class image recreate(point_t(width, height), alignment, alloc_in); } - void recreate(const point_t& dims, const Pixel& p_in, std::size_t alignment, const Alloc alloc_in) + void recreate(point_t const& dims, const Pixel& p_in, std::size_t alignment, const Alloc alloc_in) { if (dims == _view.dimensions() && _align_in_bytes == alignment && alloc_in == _alloc) return; @@ -369,7 +369,7 @@ class image catch (...) { deallocate(); throw; } } - void allocate_and_fill(const point_t& dimensions, Pixel const& p_in) + void allocate_and_fill(point_t const& dimensions, Pixel const& p_in) { try { @@ -380,7 +380,7 @@ class image } template - void allocate_and_copy(const point_t& dimensions, View const& v) + void allocate_and_copy(point_t const& dimensions, View const& v) { try { @@ -545,12 +545,17 @@ bool operator!=(const image& im1,const image inline -const typename image::view_t& view(image& img) { return img._view; } +template +inline auto view(image& img) + -> typename image::view_t const& +{ + return img._view; +} /// \brief Returns the constant-pixel view of an image -template inline -const typename image::const_view_t const_view(const image& img) +template +inline auto const_view(const image& img) + -> typename image::const_view_t const { return static_cast::const_view_t>(img._view); } diff --git a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp index 23bfb64547..1b5de48682 100644 --- a/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp +++ b/include/boost/gil/image_processing/adaptive_histogram_equalization.hpp @@ -116,8 +116,7 @@ void clip_and_redistribute(SrcHist const& src_hist, DstHist& dst_hist, double cl } } -} // namespace detail - +} // namespace detail /// \fn void non_overlapping_interpolated_clahe /// \ingroup AHE diff --git a/include/boost/gil/image_processing/filter.hpp b/include/boost/gil/image_processing/filter.hpp index 574c4cde5a..1fc4902323 100644 --- a/include/boost/gil/image_processing/filter.hpp +++ b/include/boost/gil/image_processing/filter.hpp @@ -21,9 +21,6 @@ #include #include - - - namespace boost { namespace gil { template diff --git a/include/boost/gil/image_processing/histogram_equalization.hpp b/include/boost/gil/image_processing/histogram_equalization.hpp index 033dd611b5..6ce3c5ef34 100644 --- a/include/boost/gil/image_processing/histogram_equalization.hpp +++ b/include/boost/gil/image_processing/histogram_equalization.hpp @@ -17,7 +17,6 @@ namespace boost { namespace gil { - ///////////////////////////////////////// /// Histogram Equalization(HE) ///////////////////////////////////////// @@ -42,7 +41,8 @@ namespace boost { namespace gil { /// and returns the color map used for histogram equalization. /// template -std::map histogram_equalization(histogram const& src_hist) +auto histogram_equalization(histogram const& src_hist) + -> std::map { histogram dst_hist; return histogram_equalization(src_hist, dst_hist); @@ -59,8 +59,8 @@ std::map histogram_equalization(histogram co /// as well as transforming the destination histogram. /// template -std::map - histogram_equalization(histogram const& src_hist, histogram& dst_hist) +auto histogram_equalization(histogram const& src_hist, histogram& dst_hist) + -> std::map { static_assert( std::is_integral::value && diff --git a/include/boost/gil/image_processing/histogram_matching.hpp b/include/boost/gil/image_processing/histogram_matching.hpp index 4ae5c10399..25107a7de6 100644 --- a/include/boost/gil/image_processing/histogram_matching.hpp +++ b/include/boost/gil/image_processing/histogram_matching.hpp @@ -43,8 +43,8 @@ namespace boost { namespace gil { /// reference histogram and returns the color map used for histogram matching. /// template -std::map - histogram_matching(histogram const& src_hist, histogram const& ref_hist) +auto histogram_matching(histogram const& src_hist, histogram const& ref_hist) + -> std::map { histogram dst_hist; return histogram_matching(src_hist, ref_hist, dst_hist); @@ -63,10 +63,11 @@ std::map /// matching as well as transforming the destination histogram. /// template -std::map histogram_matching( +auto histogram_matching( histogram const& src_hist, histogram const& ref_hist, histogram& dst_hist) + -> std::map { static_assert( std::is_integral::value && diff --git a/include/boost/gil/image_processing/morphology.hpp b/include/boost/gil/image_processing/morphology.hpp index e2e1926ab4..babe1acb89 100644 --- a/include/boost/gil/image_processing/morphology.hpp +++ b/include/boost/gil/image_processing/morphology.hpp @@ -13,17 +13,14 @@ #include #include -namespace boost -{ -namespace gil -{ -namespace detail -{ +namespace boost { namespace gil { namespace detail { + enum class morphological_operation { dilation, erosion, }; + /// \addtogroup ImageProcessing /// @{ diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index 3ac989ccb9..8507e0e294 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -89,7 +89,8 @@ inline void compute_tensor_entries( /// in which all entries will be equal to /// \code 1 / (dst.size()) \endcode template > -inline detail::kernel_2d generate_normalized_mean(std::size_t side_length) +inline auto generate_normalized_mean(std::size_t side_length) + -> detail::kernel_2d { if (side_length % 2 != 1) throw std::invalid_argument("kernel dimensions should be odd and equal"); @@ -108,7 +109,8 @@ inline detail::kernel_2d generate_normalized_mean(std::size_t side /// /// Fills supplied view with 1s (ones) template > -inline detail::kernel_2d generate_unnormalized_mean(std::size_t side_length) +inline auto generate_unnormalized_mean(std::size_t side_length) + -> detail::kernel_2d { if (side_length % 2 != 1) throw std::invalid_argument("kernel dimensions should be odd and equal"); @@ -127,12 +129,12 @@ inline detail::kernel_2d generate_unnormalized_mean(std::size_t si /// Fills supplied view with values taken from Gaussian distribution. See /// https://en.wikipedia.org/wiki/Gaussian_blur template > -inline detail::kernel_2d generate_gaussian_kernel(std::size_t side_length, double sigma) +inline auto generate_gaussian_kernel(std::size_t side_length, double sigma) + -> detail::kernel_2d { if (side_length % 2 != 1) throw std::invalid_argument("kernel dimensions should be odd and equal"); - const double denominator = 2 * boost::gil::detail::pi * sigma * sigma; auto middle = side_length / 2; std::vector values(side_length * side_length); @@ -160,7 +162,8 @@ inline detail::kernel_2d generate_gaussian_kernel(std::size_t side /// to obtain the desired degree). /// https://www.researchgate.net/publication/239398674_An_Isotropic_3_3_Image_Gradient_Operator template > -inline detail::kernel_2d generate_dx_sobel(unsigned int degree = 1) +inline auto generate_dx_sobel(unsigned int degree = 1) + -> detail::kernel_2d { switch (degree) { @@ -190,7 +193,8 @@ inline detail::kernel_2d generate_dx_sobel(unsigned int degree = 1 /// to obtain the desired degree). /// https://www.researchgate.net/profile/Hanno_Scharr/publication/220955743_Optimal_Filters_for_Extended_Optical_Flow/links/004635151972eda98f000000/Optimal-Filters-for-Extended-Optical-Flow.pdf template > -inline detail::kernel_2d generate_dx_scharr(unsigned int degree = 1) +inline auto generate_dx_scharr(unsigned int degree = 1) + -> detail::kernel_2d { switch (degree) { @@ -220,7 +224,8 @@ inline detail::kernel_2d generate_dx_scharr(unsigned int degree = /// to obtain the desired degree). /// https://www.researchgate.net/publication/239398674_An_Isotropic_3_3_Image_Gradient_Operator template > -inline detail::kernel_2d generate_dy_sobel(unsigned int degree = 1) +inline auto generate_dy_sobel(unsigned int degree = 1) + -> detail::kernel_2d { switch (degree) { @@ -250,7 +255,8 @@ inline detail::kernel_2d generate_dy_sobel(unsigned int degree = 1 /// to obtain the desired degree). /// https://www.researchgate.net/profile/Hanno_Scharr/publication/220955743_Optimal_Filters_for_Extended_Optical_Flow/links/004635151972eda98f000000/Optimal-Filters-for-Extended-Optical-Flow.pdf template > -inline detail::kernel_2d generate_dy_scharr(unsigned int degree = 1) +inline auto generate_dy_scharr(unsigned int degree = 1) + -> detail::kernel_2d { switch (degree) { diff --git a/include/boost/gil/image_view_factory.hpp b/include/boost/gil/image_view_factory.hpp index 7d7774c526..73c2da6ecf 100644 --- a/include/boost/gil/image_view_factory.hpp +++ b/include/boost/gil/image_view_factory.hpp @@ -54,9 +54,11 @@ struct dynamic_xy_step_transposed_type /// \ingroup ImageViewConstructors /// \brief Constructing image views from raw interleaved pixel data template -typename type_from_x_iterator::view_t -interleaved_view(std::size_t width, std::size_t height, - Iterator pixels, std::ptrdiff_t rowsize_in_bytes) { +auto interleaved_view( + std::size_t width, std::size_t height, + Iterator pixels, std::ptrdiff_t rowsize_in_bytes) + -> typename type_from_x_iterator::view_t +{ using RView = typename type_from_x_iterator::view_t; return RView(width, height, typename RView::locator(pixels, rowsize_in_bytes)); } @@ -64,8 +66,9 @@ interleaved_view(std::size_t width, std::size_t height, /// \ingroup ImageViewConstructors /// \brief Constructing image views from raw interleaved pixel data template -auto interleaved_view(point dim, Iterator pixels, - std::ptrdiff_t rowsize_in_bytes) +auto interleaved_view( + point dim, Iterator pixels, + std::ptrdiff_t rowsize_in_bytes) -> typename type_from_x_iterator::view_t { using RView = typename type_from_x_iterator::view_t; @@ -77,23 +80,32 @@ auto interleaved_view(point dim, Iterator pixels, ///////////////////////////// namespace detail { - template struct channel_pointer_type_impl; + template + struct channel_pointer_type_impl; - template struct channel_pointer_type_impl { - using type = typename channel_type::type *; + template + struct channel_pointer_type_impl + { + using type = typename channel_type::type*; }; - template struct channel_pointer_type_impl { - using type = const typename channel_type::type *; + + template + struct channel_pointer_type_impl + { + using type = const typename channel_type::type*; }; - template struct channel_pointer_type + template + struct channel_pointer_type : public channel_pointer_type_impl::value> {}; } // namespace detail /// \ingroup ImageViewConstructors /// \brief Returns C pointer to the the channels of an interleaved homogeneous view. template -typename detail::channel_pointer_type::type interleaved_view_get_raw_data(const HomogeneousView& view) { +auto interleaved_view_get_raw_data(HomogeneousView const& view) + -> typename detail::channel_pointer_type::type +{ static_assert(!is_planar::value && view_is_basic::value, ""); static_assert(std::is_pointer::value, ""); @@ -103,7 +115,9 @@ typename detail::channel_pointer_type::type interleaved_view_ge /// \ingroup ImageViewConstructors /// \brief Returns C pointer to the the channels of a given color plane of a planar homogeneous view. template -typename detail::channel_pointer_type::type planar_view_get_raw_data(const HomogeneousView& view, int plane_index) { +auto planar_view_get_raw_data(HomogeneousView const& view, int plane_index) + -> typename detail::channel_pointer_type::type +{ static_assert(is_planar::value && view_is_basic::value, ""); return dynamic_at_c(view.row_begin(0),plane_index); } @@ -167,15 +181,18 @@ struct color_converted_view_type : public detail::_color_converted_view_type -inline typename color_converted_view_type::type color_converted_view(const View& src,CC cc) { +inline auto color_converted_view(View const& src,CC cc) + -> typename color_converted_view_type::type +{ return color_converted_view_type::make(src,cc); } /// \ingroup ImageViewTransformationsColorConvert /// \brief overload of generic color_converted_view with the default color-converter template -inline typename color_converted_view_type::type -color_converted_view(const View& src) { +inline auto color_converted_view(View const& src) + -> typename color_converted_view_type::type +{ return color_converted_view(src,default_color_converter()); } @@ -185,7 +202,9 @@ color_converted_view(const View& src) { /// \ingroup ImageViewTransformationsFlipUD template -inline typename dynamic_y_step_type::type flipped_up_down_view(const View& src) { +inline auto flipped_up_down_view(View const& src) + -> typename dynamic_y_step_type::type +{ using RView = typename dynamic_y_step_type::type; return RView(src.dimensions(),typename RView::xy_locator(src.xy_at(0,src.height()-1),-1)); } @@ -196,7 +215,9 @@ inline typename dynamic_y_step_type::type flipped_up_down_view(const View& /// \ingroup ImageViewTransformationsFlipLR template -inline typename dynamic_x_step_type::type flipped_left_right_view(const View& src) { +inline auto flipped_left_right_view(View const& src) + -> typename dynamic_x_step_type::type +{ using RView = typename dynamic_x_step_type::type; return RView(src.dimensions(),typename RView::xy_locator(src.xy_at(src.width()-1,0),-1,1)); } @@ -207,7 +228,9 @@ inline typename dynamic_x_step_type::type flipped_left_right_view(const Vi /// \ingroup ImageViewTransformationsTransposed template -inline typename dynamic_xy_step_transposed_type::type transposed_view(const View& src) { +inline auto transposed_view(View const& src) + -> typename dynamic_xy_step_transposed_type::type +{ using RView = typename dynamic_xy_step_transposed_type::type; return RView(src.height(),src.width(),typename RView::xy_locator(src.xy_at(0,0),1,1,true)); } @@ -218,7 +241,9 @@ inline typename dynamic_xy_step_transposed_type::type transposed_view(cons /// \ingroup ImageViewTransformations90CW template -inline typename dynamic_xy_step_transposed_type::type rotated90cw_view(const View& src) { +inline auto rotated90cw_view(View const& src) + -> typename dynamic_xy_step_transposed_type::type +{ using RView = typename dynamic_xy_step_transposed_type::type; return RView(src.height(),src.width(),typename RView::xy_locator(src.xy_at(0,src.height()-1),-1,1,true)); } @@ -229,7 +254,9 @@ inline typename dynamic_xy_step_transposed_type::type rotated90cw_view(con /// \ingroup ImageViewTransformations90CCW template -inline typename dynamic_xy_step_transposed_type::type rotated90ccw_view(const View& src) { +inline auto rotated90ccw_view(View const& src) + -> typename dynamic_xy_step_transposed_type::type +{ using RView = typename dynamic_xy_step_transposed_type::type; return RView(src.height(),src.width(),typename RView::xy_locator(src.xy_at(src.width()-1,0),1,-1,true)); } @@ -240,7 +267,9 @@ inline typename dynamic_xy_step_transposed_type::type rotated90ccw_view(co /// \ingroup ImageViewTransformations180 template -inline typename dynamic_xy_step_type::type rotated180_view(const View& src) { +inline auto rotated180_view(View const& src) + -> typename dynamic_xy_step_type::type +{ using RView = typename dynamic_xy_step_type::type; return RView(src.dimensions(),typename RView::xy_locator(src.xy_at(src.width()-1,src.height()-1),-1,-1)); } @@ -309,7 +338,7 @@ namespace detail { struct __nth_channel_view_basic { using type = typename view_type::type, gray_layout_t, false, true, view_is_mutable::value>::type; - static type make(const View& src, int n) { + static type make(View const& src, int n) { using locator_t = typename type::xy_locator; using x_iterator_t = typename type::x_iterator; using x_iterator_base_t = typename iterator_adaptor_get_base::type; @@ -322,7 +351,7 @@ namespace detail { template struct __nth_channel_view_basic { using type = typename view_type::type, gray_layout_t, false, false, view_is_mutable::value>::type; - static type make(const View& src, int n) { + static type make(View const& src, int n) { using x_iterator_t = typename type::x_iterator; return interleaved_view(src.width(),src.height(),(x_iterator_t)&(src(0,0)[n]), src.pixels().row_size()); } @@ -346,7 +375,7 @@ namespace detail { public: using type = typename __nth_channel_view_basic::type; - static type make(const View& src, int n) { + static type make(View const& src, int n) { return __nth_channel_view_basic::make(src,n); } }; @@ -375,11 +404,13 @@ namespace detail { using result_type = reference; nth_channel_deref_fn(int n=0) : _n(n) {} - template nth_channel_deref_fn(const nth_channel_deref_fn

            & d) : _n(d._n) {} + template + nth_channel_deref_fn(const nth_channel_deref_fn

            & d) : _n(d._n) {} int _n; // the channel to use - result_type operator()(argument_type srcP) const { + auto operator()(argument_type srcP) const -> result_type + { return result_type(srcP[_n]); } }; @@ -390,7 +421,7 @@ namespace detail { using AD = typename View::template add_deref; public: using type = typename AD::type; - static type make(const View& src, int n) { + static type make(View const& src, int n) { return AD::make(src, deref_t(n)); } }; @@ -409,22 +440,15 @@ struct nth_channel_view_type { using VB = detail::__nth_channel_view::value>; public: using type = typename VB::type; - static type make(const View& src, int n) { return VB::make(src,n); } + static type make(View const& src, int n) { return VB::make(src,n); } }; - /// \ingroup ImageViewTransformationsNthChannel template -typename nth_channel_view_type::type nth_channel_view(const View& src, int n) { +typename nth_channel_view_type::type nth_channel_view(View const& src, int n) { return nth_channel_view_type::make(src,n); } - - - - - - /// \defgroup ImageViewTransformationsKthChannel kth_channel_view /// \ingroup ImageViewTransformations /// \brief single-channel (grayscale) view of the K-th channel of a given image_view. The channel index is a template parameter @@ -441,7 +465,7 @@ namespace detail { public: using type = typename view_type::value>::type; - static type make(const View& src) { + static type make(View const& src) { using locator_t = typename type::xy_locator; using x_iterator_t = typename type::x_iterator; using x_iterator_base_t = typename iterator_adaptor_get_base::type; @@ -457,7 +481,7 @@ namespace detail { using channel_t = typename kth_element_type::type; public: using type = typename view_type::value>::type; - static type make(const View& src) { + static type make(View const& src) { using x_iterator_t = typename type::x_iterator; return interleaved_view(src.width(),src.height(),(x_iterator_t)&gil::at_c(src(0,0)), src.pixels().row_size()); } @@ -480,7 +504,7 @@ namespace detail { public: using type = typename __kth_channel_view_basic::type; - static type make(const View& src) { + static type make(View const& src) { return __kth_channel_view_basic::make(src); } }; @@ -512,7 +536,8 @@ namespace detail { using result_type = reference; kth_channel_deref_fn() {} - template kth_channel_deref_fn(const kth_channel_deref_fn&) {} + template + kth_channel_deref_fn(const kth_channel_deref_fn&) {} result_type operator()(argument_type srcP) const { return result_type(gil::at_c(srcP)); @@ -525,7 +550,7 @@ namespace detail { using AD = typename View::template add_deref; public: using type = typename AD::type; - static type make(const View& src) { + static type make(View const& src) { return AD::make(src, deref_t()); } }; @@ -544,12 +569,14 @@ struct kth_channel_view_type { using VB = detail::__kth_channel_view::value>; public: using type = typename VB::type; - static type make(const View& src) { return VB::make(src); } + static type make(View const& src) { return VB::make(src); } }; /// \ingroup ImageViewTransformationsKthChannel template -typename kth_channel_view_type::type kth_channel_view(const View& src) { +auto kth_channel_view(View const& src) + -> typename kth_channel_view_type::type +{ return kth_channel_view_type::make(src); } diff --git a/include/boost/gil/io/base.hpp b/include/boost/gil/io/base.hpp index ab9294c114..fd649a371b 100644 --- a/include/boost/gil/io/base.hpp +++ b/include/boost/gil/io/base.hpp @@ -44,8 +44,8 @@ struct image_read_settings_base , _dim ( 0, 0 ) {} - image_read_settings_base( const point_t& top_left - , const point_t& dim + image_read_settings_base( point_t const& top_left + , point_t const& dim ) : _top_left( top_left ) , _dim ( dim ) @@ -54,8 +54,8 @@ struct image_read_settings_base public: - void set( const point_t& top_left - , const point_t& dim + void set( point_t const& top_left + , point_t const& dim ) { _top_left = top_left; diff --git a/include/boost/gil/io/device.hpp b/include/boost/gil/io/device.hpp index 88dade0ace..41865001f4 100644 --- a/include/boost/gil/io/device.hpp +++ b/include/boost/gil/io/device.hpp @@ -120,8 +120,8 @@ class file_stream_device ) {} - FILE* get() { return _file.get(); } - const FILE* get() const { return _file.get(); } + auto get() -> FILE* { return _file.get(); } + auto get() const -> FILE const* { return _file.get(); } int getc_unchecked() { @@ -140,9 +140,7 @@ class file_stream_device } ///@todo: change byte_t* to void* - std::size_t read( byte_t* data - , std::size_t count - ) + auto read(byte_t* data, std::size_t count) -> std::size_t { std::size_t num_elements = fread( data , 1 @@ -162,9 +160,7 @@ class file_stream_device } /// Reads array - template< typename T - , int N - > + template< typename T, int N> void read( T (&buf)[N] ) { io_error_if( read( buf, N ) < N @@ -201,9 +197,7 @@ class file_stream_device /// Writes number of elements from a buffer template < typename T > - std::size_t write( const T* buf - , std::size_t count - ) + auto write(T const* buf, std::size_t count) -> std::size_t { std::size_t num_elements = fwrite( buf , buff_item::size diff --git a/include/boost/gil/io/make_dynamic_image_writer.hpp b/include/boost/gil/io/make_dynamic_image_writer.hpp index 2ef5f3e95d..c3d6dae30f 100644 --- a/include/boost/gil/io/make_dynamic_image_writer.hpp +++ b/include/boost/gil/io/make_dynamic_image_writer.hpp @@ -39,12 +39,8 @@ auto make_dynamic_image_writer( template< typename FormatTag > inline -typename get_dynamic_image_writer< std::wstring - , FormatTag - >::type -make_dynamic_image_writer( const std::wstring& file_name - , const image_write_info< FormatTag >& info - ) +auto make_dynamic_image_writer(std::wstring const& file_name, image_write_info const& info) + -> typename get_dynamic_image_writer< std::wstring, FormatTag>::type { const char* str = detail::convert_to_native_string( file_name ); @@ -63,21 +59,13 @@ make_dynamic_image_writer( const std::wstring& file_name ); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT -template< typename FormatTag > +template inline -typename get_dynamic_image_writer< std::wstring - , FormatTag - >::type -make_dynamic_image_writer( detail::filesystem::path const& path - , image_write_info const& info - ) +auto make_dynamic_image_writer(detail::filesystem::path const& path, image_write_info const& info) + -> typename get_dynamic_image_writer::type { - return make_dynamic_image_writer( path.wstring() - , info - ); + return make_dynamic_image_writer(path.wstring(), info); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT template inline @@ -114,36 +102,23 @@ auto make_dynamic_image_writer(String const& file_name, FormatTag const&, return make_dynamic_image_writer(file_name, image_write_info()); } -template< typename FormatTag > +template inline -typename get_dynamic_image_writer< std::wstring - , FormatTag - >::type -make_dynamic_image_writer( const std::wstring& file_name - , const FormatTag& - ) +auto make_dynamic_image_writer(std::wstring const& file_name, FormatTag const&) + -> typename get_dynamic_image_writer::type { return make_dynamic_image_writer( file_name , image_write_info< FormatTag >() ); } -#ifdef BOOST_GIL_IO_ADD_FS_PATH_SUPPORT -template< typename FormatTag > +template inline -typename get_dynamic_image_writer< std::wstring - , FormatTag - >::type -make_dynamic_image_writer( const filesystem::path& path - , const FormatTag& - ) +auto make_dynamic_image_writer(detail::filesystem::path const& path, FormatTag const&) + -> typename get_dynamic_image_writer::type { - return make_dynamic_image_writer( path.wstring() - , image_write_info< FormatTag >() - ); + return make_dynamic_image_writer(path.wstring(), image_write_info()); } -#endif // BOOST_GIL_IO_ADD_FS_PATH_SUPPORT - template inline diff --git a/include/boost/gil/io/make_reader.hpp b/include/boost/gil/io/make_reader.hpp index 6a0ca9479b..cff8a678fd 100644 --- a/include/boost/gil/io/make_reader.hpp +++ b/include/boost/gil/io/make_reader.hpp @@ -39,18 +39,10 @@ auto make_reader( typename get_reader::type(device, settings); } -template< typename FormatTag - , typename ConversionPolicy - > +template inline -typename get_reader< std::wstring - , FormatTag - , ConversionPolicy - >::type -make_reader( const std::wstring& file_name - , const image_read_settings< FormatTag >& settings - , const ConversionPolicy& - ) +auto make_reader(std::wstring const &file_name, image_read_settings const& settings, ConversionPolicy const&) + -> typename get_reader::type { const char* str = detail::convert_to_native_string( file_name ); @@ -70,23 +62,12 @@ make_reader( const std::wstring& file_name ); } -template< typename FormatTag - , typename ConversionPolicy - > +template inline -typename get_reader< std::wstring - , FormatTag - , ConversionPolicy - >::type -make_reader( detail::filesystem::path const& path - , const image_read_settings< FormatTag >& settings - , const ConversionPolicy& cc - ) +auto make_reader(detail::filesystem::path const& path, image_read_settings const& settings, ConversionPolicy const& cc) + -> typename get_reader::type { - return make_reader( path.wstring() - , settings - , cc - ); + return make_reader(path.wstring(), settings, cc); } template @@ -132,18 +113,10 @@ auto make_reader( return make_reader(file_name, image_read_settings(), cc); } -template< typename FormatTag - , typename ConversionPolicy - > +template inline -typename get_reader< std::wstring - , FormatTag - , ConversionPolicy - >::type -make_reader( const std::wstring& file_name - , const FormatTag& - , const ConversionPolicy& cc - ) +auto make_reader(std::wstring const &file_name, FormatTag const&, ConversionPolicy const& cc) + -> typename get_reader::type { return make_reader( file_name , image_read_settings< FormatTag >() @@ -151,23 +124,12 @@ make_reader( const std::wstring& file_name ); } -template< typename FormatTag - , typename ConversionPolicy - > +template inline -typename get_reader< std::wstring - , FormatTag - , ConversionPolicy - >::type -make_reader( detail::filesystem::path const& path - , const FormatTag& - , const ConversionPolicy& cc - ) +auto make_reader(detail::filesystem::path const& path, FormatTag const&, ConversionPolicy const& cc) + -> typename get_reader::type { - return make_reader( path.wstring() - , image_read_settings< FormatTag >() - , cc - ); + return make_reader(path.wstring(), image_read_settings(), cc); } template diff --git a/include/boost/gil/io/make_scanline_reader.hpp b/include/boost/gil/io/make_scanline_reader.hpp index 73e45a54c6..3c630aaa65 100644 --- a/include/boost/gil/io/make_scanline_reader.hpp +++ b/include/boost/gil/io/make_scanline_reader.hpp @@ -37,14 +37,10 @@ auto make_scanline_reader(String const& file_name, FormatTag const&, device, image_read_settings()); } -template< typename FormatTag > +template inline -typename get_scanline_reader< std::wstring - , FormatTag - >::type -make_scanline_reader( const std::wstring& file_name - , FormatTag const& - ) +auto make_scanline_reader(std::wstring const& file_name, FormatTag const&) + -> typename get_scanline_reader::type { const char* str = detail::convert_to_native_string( file_name ); @@ -63,18 +59,12 @@ make_scanline_reader( const std::wstring& file_name ); } -template< typename FormatTag > +template inline -typename get_scanline_reader< std::wstring - , FormatTag - >::type -make_scanline_reader( detail::filesystem::path const& path - , FormatTag const& - ) +auto make_scanline_reader(detail::filesystem::path const& path, FormatTag const&) + -> typename get_scanline_reader::type { - return make_scanline_reader( path.wstring() - , image_read_settings< FormatTag >() - ); + return make_scanline_reader(path.wstring(), image_read_settings()); } template diff --git a/include/boost/gil/io/make_writer.hpp b/include/boost/gil/io/make_writer.hpp index 69025d8778..b7458fcfad 100644 --- a/include/boost/gil/io/make_writer.hpp +++ b/include/boost/gil/io/make_writer.hpp @@ -34,14 +34,10 @@ auto make_writer(String const& file_name, image_write_info const& inf return typename get_writer::type(device, info); } -template< typename FormatTag > +template inline -typename get_writer< std::wstring - , FormatTag - >::type -make_writer( const std::wstring& file_name - , const image_write_info< FormatTag >& info - ) +auto make_writer(std::wstring const& file_name, image_write_info const& info) + -> typename get_writer::type { const char* str = detail::convert_to_native_string( file_name ); @@ -60,18 +56,12 @@ make_writer( const std::wstring& file_name ); } -template< typename FormatTag > +template inline -typename get_writer< std::wstring - , FormatTag - >::type -make_writer( detail::filesystem::path const& path - , const image_write_info< FormatTag >& info - ) +auto make_writer(detail::filesystem::path const& path, image_write_info const& info) + -> typename get_writer::type { - return make_writer( path.wstring() - , info - ); + return make_writer(path.wstring(), info); } template @@ -109,32 +99,22 @@ auto make_writer(String const& file_name, FormatTag const&, return make_writer(file_name, image_write_info()); } -template< typename FormatTag > +template inline -typename get_writer< std::wstring - , FormatTag - >::type -make_writer( const std::wstring& file_name - , const FormatTag& - ) +auto make_writer(std::wstring const &file_name, FormatTag const&) + -> typename get_writer::type { return make_writer( file_name , image_write_info< FormatTag >() ); } -template< typename FormatTag > +template inline -typename get_writer< std::wstring - , FormatTag - >::type -make_writer( detail::filesystem::path const& path - , const FormatTag& tag - ) +auto make_writer(detail::filesystem::path const& path, FormatTag const& tag) + -> typename get_writer::type { - return make_writer( path.wstring() - , tag - ); + return make_writer(path.wstring(), tag); } template diff --git a/include/boost/gil/io/path_spec.hpp b/include/boost/gil/io/path_spec.hpp index 8ffbc2129a..7b0c91d2f3 100644 --- a/include/boost/gil/io/path_spec.hpp +++ b/include/boost/gil/io/path_spec.hpp @@ -21,7 +21,7 @@ template<> struct is_supported_path_spec< std::string > : std::true_type template<> struct is_supported_path_spec< const std::string > : std::true_type {}; template<> struct is_supported_path_spec< std::wstring > : std::true_type {}; template<> struct is_supported_path_spec< const std::wstring > : std::true_type {}; -template<> struct is_supported_path_spec< const char* > : std::true_type {}; +template<> struct is_supported_path_spec< char const* > : std::true_type {}; template<> struct is_supported_path_spec< char* > : std::true_type {}; template<> struct is_supported_path_spec< const wchar_t* > : std::true_type {}; template<> struct is_supported_path_spec< wchar_t* > : std::true_type {}; @@ -48,7 +48,7 @@ inline std::string convert_to_string( std::wstring const& s ) return std::string( c, c + len ); } -inline std::string convert_to_string( const char* str ) +inline std::string convert_to_string( char const* str ) { return std::string( str ); } @@ -63,22 +63,22 @@ inline std::string convert_to_string(filesystem::path const& path) return convert_to_string(path.string()); } -inline const char* convert_to_native_string( char* str ) +inline char const* convert_to_native_string( char* str ) { return str; } -inline const char* convert_to_native_string( const char* str ) +inline char const* convert_to_native_string( char const* str ) { return str; } -inline const char* convert_to_native_string( const std::string& str ) +inline char const* convert_to_native_string( const std::string& str ) { return str.c_str(); } -inline const char* convert_to_native_string( const wchar_t* str ) +inline char const* convert_to_native_string( const wchar_t* str ) { std::size_t len = wcslen( str ) + 1; char* c = new char[len]; @@ -87,7 +87,7 @@ inline const char* convert_to_native_string( const wchar_t* str ) return c; } -inline const char* convert_to_native_string( const std::wstring& str ) +inline char const* convert_to_native_string( std::wstring const& str ) { std::size_t len = wcslen( str.c_str() ) + 1; char* c = new char[len]; diff --git a/include/boost/gil/io/reader_base.hpp b/include/boost/gil/io/reader_base.hpp index 309331b761..cc997b0a62 100644 --- a/include/boost/gil/io/reader_base.hpp +++ b/include/boost/gil/io/reader_base.hpp @@ -73,7 +73,7 @@ struct reader_base private: - void setup( const point_t& /* dim */ ) + void setup( point_t const& /* dim */ ) { //check_coordinates( dim ); @@ -88,7 +88,7 @@ struct reader_base //} } - void check_coordinates( const point_t& /* dim */ ) + void check_coordinates( point_t const& /* dim */ ) { //using int_t = point_t::value_type; diff --git a/include/boost/gil/locator.hpp b/include/boost/gil/locator.hpp index 095bc6fac7..776eedd336 100644 --- a/include/boost/gil/locator.hpp +++ b/include/boost/gil/locator.hpp @@ -140,7 +140,7 @@ class pixel_2d_locator_base template typename axis::iterator& axis_iterator() { return detail::locator_axis()(concrete()); } template typename axis::iterator const& axis_iterator() const { return detail::locator_axis()(concrete()); } - template typename axis::iterator axis_iterator(const point_t& p) const { return detail::locator_axis()(concrete(),p); } + template typename axis::iterator axis_iterator(point_t const& p) const { return detail::locator_axis()(concrete(),p); } reference operator()(x_coord_t dx, y_coord_t dy) const { return *x_at(dx,dy); } reference operator[](const difference_type& d) const { return *x_at(d.x,d.y); } @@ -176,8 +176,8 @@ namespace detail { inline iterator& operator()( Loc& loc) const { return loc.x(); } inline iterator const& operator()(const Loc& loc) const { return loc.x(); } - inline iterator operator()( Loc& loc, const point_t& d) const { return loc.x_at(d); } - inline iterator operator()(const Loc& loc, const point_t& d) const { return loc.x_at(d); } + inline iterator operator()( Loc& loc, point_t const& d) const { return loc.x_at(d); } + inline iterator operator()(const Loc& loc, point_t const& d) const { return loc.x_at(d); } }; template @@ -189,8 +189,8 @@ namespace detail { inline iterator& operator()( Loc& loc) const { return loc.y(); } inline iterator const& operator()(const Loc& loc) const { return loc.y(); } - inline iterator operator()( Loc& loc, const point_t& d) const { return loc.y_at(d); } - inline iterator operator()(const Loc& loc, const point_t& d) const { return loc.y_at(d); } + inline iterator operator()( Loc& loc, point_t const& d) const { return loc.y_at(d); } + inline iterator operator()(const Loc& loc, point_t const& d) const { return loc.y_at(d); } }; } diff --git a/include/boost/gil/pixel_iterator_adaptor.hpp b/include/boost/gil/pixel_iterator_adaptor.hpp index 093a8e9ace..36c4cf02e0 100644 --- a/include/boost/gil/pixel_iterator_adaptor.hpp +++ b/include/boost/gil/pixel_iterator_adaptor.hpp @@ -121,18 +121,20 @@ struct channel_type > : public channel_type< ///////////////////////////// template -struct byte_to_memunit > : public byte_to_memunit {}; +struct byte_to_memunit> : public byte_to_memunit {}; template -inline typename std::iterator_traits::difference_type -memunit_step(const dereference_iterator_adaptor& p) { +inline auto memunit_step(dereference_iterator_adaptor const& p) + -> typename std::iterator_traits::difference_type +{ return memunit_step(p.base()); } template -inline typename std::iterator_traits::difference_type -memunit_distance(const dereference_iterator_adaptor& p1, - const dereference_iterator_adaptor& p2) { +inline auto memunit_distance(dereference_iterator_adaptor const& p1, + dereference_iterator_adaptor const& p2) + -> typename std::iterator_traits::difference_type +{ return memunit_distance(p1.base(),p2.base()); } @@ -143,18 +145,19 @@ inline void memunit_advance(dereference_iterator_adaptor& p, } template -inline dereference_iterator_adaptor -memunit_advanced(const dereference_iterator_adaptor& p, - typename std::iterator_traits::difference_type diff) { +inline auto memunit_advanced(dereference_iterator_adaptor const& p, + typename std::iterator_traits::difference_type diff) + -> dereference_iterator_adaptor +{ return dereference_iterator_adaptor(memunit_advanced(p.base(), diff), p.deref_fn()); } template -inline -typename std::iterator_traits >::reference -memunit_advanced_ref(const dereference_iterator_adaptor& p, - typename std::iterator_traits::difference_type diff) { +inline auto memunit_advanced_ref(dereference_iterator_adaptor const& p, + typename std::iterator_traits::difference_type diff) + -> typename std::iterator_traits >::reference +{ return *memunit_advanced(p, diff); } diff --git a/include/boost/gil/planar_pixel_iterator.hpp b/include/boost/gil/planar_pixel_iterator.hpp index 0aead851b4..df4152b68f 100644 --- a/include/boost/gil/planar_pixel_iterator.hpp +++ b/include/boost/gil/planar_pixel_iterator.hpp @@ -201,10 +201,16 @@ struct channel_type> ///////////////////////////// template -inline std::ptrdiff_t memunit_step(const planar_pixel_iterator&) { return sizeof(typename std::iterator_traits::value_type); } +inline auto memunit_step(planar_pixel_iterator const&) + -> std::ptrdiff_t +{ + return sizeof(typename std::iterator_traits::value_type); +} template -inline std::ptrdiff_t memunit_distance(const planar_pixel_iterator& p1, const planar_pixel_iterator& p2) { +inline auto memunit_distance(planar_pixel_iterator const& p1, planar_pixel_iterator const& p2) + -> std::ptrdiff_t +{ return memunit_distance(gil::at_c<0>(p1),gil::at_c<0>(p2)); } @@ -222,15 +228,18 @@ inline void memunit_advance(planar_pixel_iterator& p, std::ptrdiff_t diff) } template -inline planar_pixel_iterator memunit_advanced(const planar_pixel_iterator& p, std::ptrdiff_t diff) { +inline auto memunit_advanced(planar_pixel_iterator const& p, std::ptrdiff_t diff) + -> planar_pixel_iterator +{ planar_pixel_iterator ret=p; memunit_advance(ret, diff); return ret; } template -inline planar_pixel_reference::reference,ColorSpace> - memunit_advanced_ref(const planar_pixel_iterator& ptr, std::ptrdiff_t diff) { +inline auto memunit_advanced_ref(planar_pixel_iterator const& ptr, std::ptrdiff_t diff) + -> planar_pixel_reference::reference,ColorSpace> +{ return planar_pixel_reference::reference,ColorSpace>(ptr, diff); } diff --git a/include/boost/gil/point.hpp b/include/boost/gil/point.hpp index 0f40faf46f..5b9ffacf79 100644 --- a/include/boost/gil/point.hpp +++ b/include/boost/gil/point.hpp @@ -246,44 +246,44 @@ T& axis_value(point& p) /// \ingroup PointAlgorithm template -inline point iround(point const& p) +inline auto iround(point const& p) -> point { static_assert(std::is_integral::value, "T is not integer"); return { static_cast(p.x), static_cast(p.y) }; } /// \ingroup PointAlgorithm -inline point iround(point const& p) +inline auto iround(point const& p) -> point { return { iround(p.x), iround(p.y) }; } /// \ingroup PointAlgorithm -inline point iround(point const& p) +inline auto iround(point const& p) -> point { return { iround(p.x), iround(p.y) }; } /// \ingroup PointAlgorithm -inline point ifloor(point const& p) +inline auto ifloor(point const& p) -> point { return { ifloor(p.x), ifloor(p.y) }; } /// \ingroup PointAlgorithm -inline point ifloor(point const& p) +inline auto ifloor(point const& p) -> point { return { ifloor(p.x), ifloor(p.y) }; } /// \ingroup PointAlgorithm -inline point iceil(point const& p) +inline auto iceil(point const& p) -> point { return { iceil(p.x), iceil(p.y) }; } /// \ingroup PointAlgorithm -inline point iceil(point const& p) +inline auto iceil(point const& p) -> point { return { iceil(p.x), iceil(p.y) }; } diff --git a/include/boost/gil/position_iterator.hpp b/include/boost/gil/position_iterator.hpp index 8d8880438e..7685035058 100644 --- a/include/boost/gil/position_iterator.hpp +++ b/include/boost/gil/position_iterator.hpp @@ -43,20 +43,34 @@ struct position_iterator : public iterator_facade, using point_t = typename Deref::argument_type; position_iterator() {} - position_iterator(const point_t& p, const point_t& step, const Deref& d) : _p(p), _step(step), _d(d) {} - - position_iterator(const position_iterator& p) : _p(p._p), _step(p._step), _d(p._d) {} - template position_iterator(const position_iterator& p) : _p(p._p), _step(p._step), _d(p._d) {} - position_iterator& operator=(const position_iterator& p) { _p=p._p; _d=p._d; _step=p._step; return *this; } - - const point_t& pos() const { return _p; } - const point_t& step() const { return _step; } - const Deref& deref_fn() const { return _d; } + position_iterator(point_t const& p, point_t const& step, Deref const& d) : _p(p), _step(step), _d(d) {} + + position_iterator(position_iterator const& p) : _p(p._p), _step(p._step), _d(p._d) {} + + template + position_iterator(position_iterator const& p) : _p(p._p), _step(p._step), _d(p._d) {} + + auto operator=(position_iterator const& p) -> position_iterator& + { + _p=p._p; + _d=p._d; + _step=p._step; + return *this; + } + + auto pos() const -> point_t const& { return _p; } + auto step() const -> point_t const& { return _step; } + auto deref_fn() const -> Deref const& { return _d; } void set_step(difference_type s) { _step[Dim]=s; } /// For some reason operator[] provided by iterator_adaptor returns a custom class that is convertible to reference /// We require our own reference because it is registered in iterator_traits - reference operator[](difference_type d) const { point_t p=_p; p[Dim]+=d*_step[Dim]; return _d(p); } + auto operator[](difference_type d) const -> reference + { + point_t p=_p; + p[Dim]+=d*_step[Dim]; + return _d(p); + } private: point_t _p, _step; diff --git a/include/boost/gil/premultiply.hpp b/include/boost/gil/premultiply.hpp index 530719cd66..078c311111 100644 --- a/include/boost/gil/premultiply.hpp +++ b/include/boost/gil/premultiply.hpp @@ -98,11 +98,12 @@ struct premultiplied_view_type using add_ref_t = typename SrcView::template add_deref; public: using type = typename add_ref_t::type; // the color converted view type - static type make(const SrcView& sv) { return add_ref_t::make(sv, deref_t()); } + static type make(SrcView const& sv) { return add_ref_t::make(sv, deref_t()); } }; -template inline -typename premultiplied_view_type::type premultiply_view(const View& src) +template +inline auto premultiply_view(View const& src) + -> typename premultiplied_view_type::type { return premultiplied_view_type::make(src); } diff --git a/include/boost/gil/rgb.hpp b/include/boost/gil/rgb.hpp index 1ab15f8e90..a4d342a97f 100644 --- a/include/boost/gil/rgb.hpp +++ b/include/boost/gil/rgb.hpp @@ -42,8 +42,7 @@ using bgr_layout_t = layout>; /// \ingroup ImageViewConstructors /// \brief from raw RGB planar data template -inline -auto planar_rgb_view( +inline auto planar_rgb_view( std::size_t width, std::size_t height, IC r, IC g, IC b, std::ptrdiff_t rowsize_in_bytes) diff --git a/include/boost/gil/rgba.hpp b/include/boost/gil/rgba.hpp index eb76db8bdb..50943e166f 100644 --- a/include/boost/gil/rgba.hpp +++ b/include/boost/gil/rgba.hpp @@ -39,8 +39,7 @@ using abgr_layout_t = layout>; /// \ingroup ImageViewConstructors /// \brief from raw RGBA planar data template -inline -auto planar_rgba_view(std::size_t width, std::size_t height, +inline auto planar_rgba_view(std::size_t width, std::size_t height, ChannelPtr r, ChannelPtr g, ChannelPtr b, ChannelPtr a, std::ptrdiff_t rowsize_in_bytes) -> typename type_from_x_iterator >::view_t diff --git a/include/boost/gil/step_iterator.hpp b/include/boost/gil/step_iterator.hpp index f164746247..5472289b5e 100644 --- a/include/boost/gil/step_iterator.hpp +++ b/include/boost/gil/step_iterator.hpp @@ -47,9 +47,9 @@ class step_iterator_adaptor : public iterator_adaptor::reference; step_iterator_adaptor() {} - step_iterator_adaptor(const Iterator& it, SFn step_fn=SFn()) : parent_t(it), _step_fn(step_fn) {} + step_iterator_adaptor(Iterator const& it, SFn step_fn=SFn()) : parent_t(it), _step_fn(step_fn) {} - difference_type step() const { return _step_fn.step(); } + auto step() const -> difference_type { return _step_fn.step(); } protected: SFn _step_fn; @@ -59,7 +59,11 @@ class step_iterator_adaptor : public iterator_adaptorbase_reference(),1); } void decrement() { _step_fn.advance(this->base_reference(),-1); } void advance(base_difference_type d) { _step_fn.advance(this->base_reference(),d); } - difference_type distance_to(const step_iterator_adaptor& it) const { return _step_fn.difference(this->base_reference(),it.base_reference()); } + + auto distance_to(step_iterator_adaptor const& it) const -> difference_type + { + return _step_fn.difference(this->base_reference(),it.base_reference()); + } }; // although iterator_adaptor defines these, the default implementation computes distance and compares for zero. @@ -124,11 +128,15 @@ struct memunit_step_fn { memunit_step_fn(difference_type step=memunit_step(Iterator())) : _step(step) {} - difference_type difference(const Iterator& it1, const Iterator& it2) const { return memunit_distance(it1,it2)/_step; } - void advance(Iterator& it, difference_type d) const { memunit_advance(it,d*_step); } - difference_type step() const { return _step; } + auto difference(Iterator const& it1, Iterator const& it2) const -> difference_type + { + return memunit_distance(it1,it2)/_step; + } + + void advance(Iterator& it, difference_type d) const { memunit_advance(it,d*_step); } + auto step() const -> difference_type { return _step; } - void set_step(std::ptrdiff_t step) { _step=step; } + void set_step(std::ptrdiff_t step) { _step=step; } private: BOOST_GIL_CLASS_REQUIRE(Iterator, boost::gil, MemoryBasedIteratorConcept) difference_type _step; @@ -156,12 +164,12 @@ class memory_based_step_iterator : public detail::step_iterator_adaptor reference { return *(*this+d); } void set_step(std::ptrdiff_t memunit_step) { this->_step_fn.set_step(memunit_step); } - x_iterator& base() { return parent_t::base_reference(); } - x_iterator const& base() const { return parent_t::base_reference(); } + auto base() -> x_iterator& { return parent_t::base_reference(); } + auto base() const -> x_iterator const& { return parent_t::base_reference(); } }; template @@ -215,11 +223,12 @@ template struct byte_to_memunit> : public byte_to_memunit {}; template -inline std::ptrdiff_t memunit_step(const memory_based_step_iterator& p) { return p.step(); } +inline auto memunit_step(memory_based_step_iterator const& p) -> std::ptrdiff_t { return p.step(); } template -inline std::ptrdiff_t memunit_distance(const memory_based_step_iterator& p1, - const memory_based_step_iterator& p2) { +inline auto memunit_distance(memory_based_step_iterator const& p1, memory_based_step_iterator const& p2) + -> std::ptrdiff_t +{ return memunit_distance(p1.base(),p2.base()); } @@ -230,16 +239,16 @@ inline void memunit_advance(memory_based_step_iterator& p, } template -inline memory_based_step_iterator -memunit_advanced(const memory_based_step_iterator& p, - std::ptrdiff_t diff) { +inline auto memunit_advanced(const memory_based_step_iterator& p, std::ptrdiff_t diff) + -> memory_based_step_iterator +{ return memory_based_step_iterator(memunit_advanced(p.base(), diff),p.step()); } template -inline typename std::iterator_traits::reference -memunit_advanced_ref(const memory_based_step_iterator& p, - std::ptrdiff_t diff) { +inline auto memunit_advanced_ref(const memory_based_step_iterator& p, std::ptrdiff_t diff) + -> typename std::iterator_traits::reference +{ return memunit_advanced_ref(p.base(), diff); } @@ -259,7 +268,10 @@ struct iterator_add_deref,Deref> { using type = memory_based_step_iterator::type>; - static type make(const memory_based_step_iterator& it, const Deref& d) { return type(iterator_add_deref::make(it.base(),d),it.step()); } + static type make(const memory_based_step_iterator& it, const Deref& d) + { + return type(iterator_add_deref::make(it.base(),d),it.step()); + } }; //////////////////////////////////////////////////////////////////////////////////////// @@ -313,7 +325,9 @@ auto make_step_iterator_impl( /// The step iterator can be wrapped inside another iterator. Also, it may not have the /// type memory_based_step_iterator, but it could be a user-provided type. template // Models MemoryBasedIteratorConcept, HasDynamicXStepTypeConcept -typename dynamic_x_step_type::type make_step_iterator(const I& it, std::ptrdiff_t step) { +inline auto make_step_iterator(I const& it, std::ptrdiff_t step) + -> typename dynamic_x_step_type::type +{ return detail::make_step_iterator_impl(it, step, typename is_iterator_adaptor::type()); } diff --git a/include/boost/gil/utilities.hpp b/include/boost/gil/utilities.hpp index 5b5926ba04..988efc3209 100644 --- a/include/boost/gil/utilities.hpp +++ b/include/boost/gil/utilities.hpp @@ -152,15 +152,16 @@ class deref_compose : public deref_base // reinterpret_cast is implementation-defined. Static cast is not. template BOOST_FORCEINLINE -OutPtr gil_reinterpret_cast(In* p) +auto gil_reinterpret_cast(In* p) -> OutPtr { return static_cast(static_cast(p)); } -template BOOST_FORCEINLINE -const OutPtr gil_reinterpret_cast_c(const In* p) +template +BOOST_FORCEINLINE +auto gil_reinterpret_cast_c(In const* p) -> OutPtr const { - return static_cast(static_cast(p)); + return static_cast(static_cast(p)); } namespace detail { @@ -170,8 +171,8 @@ namespace detail { //////////////////////////////////////////////////////////////////////////////// template -std::pair _copy_n(InputIter first, Size count, - OutputIter result, std::input_iterator_tag) +auto _copy_n(InputIter first, Size count, OutputIter result, std::input_iterator_tag) + -> std::pair { for ( ; count > 0; --count) { @@ -183,23 +184,23 @@ std::pair _copy_n(InputIter first, Size count, } template -inline std::pair -_copy_n(RAIter first, Size count, OutputIter result, std::random_access_iterator_tag) +inline auto _copy_n(RAIter first, Size count, OutputIter result, std::random_access_iterator_tag) + -> std::pair { RAIter last = first + count; return std::pair(last, std::copy(first, last, result)); } template -inline std::pair -_copy_n(InputIter first, Size count, OutputIter result) +inline auto _copy_n(InputIter first, Size count, OutputIter result) + -> std::pair { return _copy_n(first, count, result, typename std::iterator_traits::iterator_category()); } template -inline std::pair -copy_n(InputIter first, Size count, OutputIter result) +inline auto copy_n(InputIter first, Size count, OutputIter result) + -> std::pair { return detail::_copy_n(first, count, result); } From 1049c07192e1cbe5377f23f31cf15f28c20863dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 27 Jun 2022 15:33:17 +0200 Subject: [PATCH 142/193] build: Fix CMake source file extensions must be explicit --- test/extension/toolbox/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/extension/toolbox/CMakeLists.txt b/test/extension/toolbox/CMakeLists.txt index 70df68db42..1fdbcb4001 100644 --- a/test/extension/toolbox/CMakeLists.txt +++ b/test/extension/toolbox/CMakeLists.txt @@ -29,7 +29,7 @@ foreach(_name set(_target test_ext_toolbox_${_name}) add_executable(${_target} "") - target_sources(${_target} PRIVATE ${_name}) + target_sources(${_target} PRIVATE "${_name}.cpp") target_link_libraries(${_target} PRIVATE gil_compile_options @@ -50,7 +50,7 @@ foreach(_name set(_target test_ext_toolbox_${_name}) add_executable(${_target} "") - target_sources(${_target} PRIVATE ${_name}) + target_sources(${_target} PRIVATE "${_name}.cpp") target_link_libraries(${_target} PRIVATE gil_compile_options From 27826a7d554d6b9b555e099d722c4d7981f8ebe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 27 Jun 2022 21:45:24 +0100 Subject: [PATCH 143/193] ci: Remove C++11 build jobs after C++14 switch (#698) * build: test/Jamfile now check for cxx14_constepxr Closes #696 --- .appveyor.yml | 14 +-------- .../steps-cmake-build-and-test.yml | 2 +- .github/workflows/ci.yml | 30 +++++++++---------- .github/workflows/coverage.yml | 2 +- test/Jamfile | 4 ++- 5 files changed, 21 insertions(+), 31 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 4ad917bd3f..6ac0c704ee 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,7 +7,7 @@ # version: 1.0.{build}-{branch} -# Current Boost.GIL develop branch (future Boost 1.68) requires C++11 +# Current Boost.GIL develop branch requires C++14 # Since VS2017, MSVC default is /std:c++14, so no explicit switch is required. image: Visual Studio 2017 @@ -17,18 +17,6 @@ shallow_clone: true environment: matrix: - - TOOLSET: msvc-14.0 - ARCH: x86_64 - VARIANT: debug - CXXSTD: 11 - TEST_HEADERS: 1 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - - TOOLSET: msvc-14.0 - ARCH: x86_64 - VARIANT: release - CXXSTD: 11 - TEST_HEADERS: 1 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - TOOLSET: msvc-14.1 ARCH: x86_64 VARIANT: debug diff --git a/.ci/azure-pipelines/steps-cmake-build-and-test.yml b/.ci/azure-pipelines/steps-cmake-build-and-test.yml index c60d6b2b26..a4454faaf0 100644 --- a/.ci/azure-pipelines/steps-cmake-build-and-test.yml +++ b/.ci/azure-pipelines/steps-cmake-build-and-test.yml @@ -7,7 +7,7 @@ parameters: # defaults, if not specified configuration: 'Release' - cxxver: '11' + cxxver: '14' enable_ext_io: 'OFF' enable_ext_numeric: 'ON' enable_ext_toolbox: 'ON' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 583d8107a7..6cf10bd9e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,64 +19,64 @@ jobs: matrix: include: - toolset: gcc-6 - cxxstd: "11,14,1z" + cxxstd: "14,1z" os: ubuntu-18.04 install: g++-6 - toolset: gcc-7 - cxxstd: "11,14,17" + cxxstd: "14,17" os: ubuntu-18.04 - toolset: gcc compiler: g++-8 - cxxstd: "11,14" + cxxstd: "14" os: ubuntu-18.04 install: g++-8 - toolset: gcc-9 - cxxstd: "11,14,17" + cxxstd: "14,17" os: ubuntu-18.04 - toolset: gcc-10 - cxxstd: "11,14,17" + cxxstd: "14,17" os: ubuntu-18.04 - toolset: clang compiler: clang++-3.9 - cxxstd: "11,14" + cxxstd: "14" os: ubuntu-18.04 install: clang-3.9 - toolset: clang compiler: clang++-4.0 - cxxstd: "11,14" + cxxstd: "14" os: ubuntu-18.04 install: clang-4.0 - toolset: clang compiler: clang++-5.0 - cxxstd: "11,14,1z" + cxxstd: "14,1z" os: ubuntu-18.04 install: clang-5.0 - toolset: clang compiler: clang++-6.0 - cxxstd: "11,14,17" + cxxstd: "14,17" os: ubuntu-18.04 install: clang-6.0 - toolset: clang compiler: clang++-7 - cxxstd: "11,14,17" + cxxstd: "14,17" os: ubuntu-18.04 install: clang-7 - toolset: clang compiler: clang++-8 - cxxstd: "11,14,17" + cxxstd: "14,17" os: ubuntu-20.04 install: clang-8 - toolset: clang compiler: clang++-9 - cxxstd: "11,14,17" + cxxstd: "14,17" os: ubuntu-20.04 install: clang-9 - toolset: clang compiler: clang++-10 - cxxstd: "11,14,17" + cxxstd: "14,17" os: ubuntu-20.04 - toolset: clang - cxxstd: "11,14,17" + cxxstd: "14,17" os: macos-10.15 runs-on: ${{matrix.os}} @@ -129,7 +129,7 @@ jobs: addrmd: 32,64 os: windows-2019 - toolset: gcc - cxxstd: "11,14,17" + cxxstd: "14,17" addrmd: 64 os: windows-2019 - toolset: msvc-14.3 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index c538be936d..3bd330aba2 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -43,7 +43,7 @@ jobs: - name: Run tests run: | cd ../boost-root - ./b2 -j3 libs/$LIBRARY/test coverage=on toolset=gcc cxxstd=11 variant=debug + ./b2 -j3 libs/$LIBRARY/test coverage=on toolset=gcc cxxstd=14 variant=debug lcov --directory bin.v2 --capture --no-external --directory $(pwd) --output-file coverage.info > /dev/null 2>&1 lcov --extract coverage.info $(pwd)'/boost/gil/*' --output-file coverage.info > /dev/null lcov --list coverage.info diff --git a/test/Jamfile b/test/Jamfile index 6acff3979c..5c674a587f 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -21,7 +21,7 @@ import testing ; # header providing std::filesystem. # You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING # to acknowledge that you have received this warning. -local msvc-cxxs-with-experimental-fs = 11 14 ; +local msvc-cxxs-with-experimental-fs = 14 ; project : @@ -32,6 +32,8 @@ project cxx11_template_aliases cxx11_trailing_result_types # implies decltype and auto cxx11_variadic_templates + cxx14_constexpr + cxx14_return_type_deduction ] . # TODO: Enable concepts check for all, not just test/core From 5c3cfc7f7b424d715dd8998ab576d479561dd00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 27 Jun 2022 21:47:47 +0100 Subject: [PATCH 144/193] docs: Bump C++11 to C++14 as current required (#700) Related to #677 --- CONTRIBUTING.md | 54 +++++++++++----------- README.md | 6 +-- doc/index.rst | 2 +- doc/installation.rst | 6 +-- example/adaptive_histogram_equalization.md | 5 +- example/adaptive_threshold.md | 5 +- example/affine.md | 5 +- example/anisotropic_diffusion.md | 5 +- example/convolution.md | 5 +- example/convolve2d.md | 5 +- example/dynamic_image.md | 5 +- example/harris.md | 5 +- example/hessian.md | 2 +- example/histogram.md | 5 +- example/histogram_equalization.md | 5 +- example/histogram_matching.md | 2 +- example/hough_transform_circle.md | 5 +- example/hough_transform_line.md | 5 +- example/interleaved_ptr.md | 5 +- example/mandelbrot.md | 5 +- example/morphology.md | 5 +- example/packed_pixel.md | 5 +- example/rasterizer_circle.md | 5 +- example/rasterizer_ellipse.md | 5 +- example/rasterizer_line.md | 5 +- example/resize.md | 5 +- example/sobel_scharr.md | 5 +- example/threshold.md | 5 +- example/tutorial_histogram.md | 5 +- example/x_gradient.md | 5 +- 30 files changed, 131 insertions(+), 61 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 403ac05ef9..78efc6134a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ please follow the workflow explained in this document. ## Prerequisites -- C++11 compiler +- C++14 compiler - Build and run-time dependencies for tests and examples: - Boost.Filesystem - Boost.Test @@ -359,25 +359,25 @@ Run core tests only specifying location of directory with tests: ```shell cd libs/gil -b2 cxxstd=11 test/core +b2 cxxstd=14 test/core ``` Run all tests for selected extension (from Boost root directory, as alternative): ```shell -b2 cxxstd=11 libs/gil/test/extension/dynamic_image -b2 cxxstd=11 libs/gil/test/extension/io -b2 cxxstd=11 libs/gil/test/extension/numeric -b2 cxxstd=11 libs/gil/test/extension/toolbox +b2 cxxstd=14 libs/gil/test/extension/dynamic_image +b2 cxxstd=14 libs/gil/test/extension/io +b2 cxxstd=14 libs/gil/test/extension/numeric +b2 cxxstd=14 libs/gil/test/extension/toolbox ``` Run I/O extension tests bundled in target called `simple`: ```shell -b2 cxxstd=11 libs/gil/test/io//simple +b2 cxxstd=14 libs/gil/test/io//simple ``` -**TIP:** Pass `b2` feature `cxxstd=11,14,17,2a` to request compilation for +**TIP:** Pass `b2` feature `cxxstd=14,17,2a` to request compilation for multiple C++ standard versions in single build run. ### Using CMake @@ -405,17 +405,17 @@ The provided CMake configuration allows a couple of ways to develop Boost.GIL: For CMake, you only need to build Boost libraries required by Boost.Test library which is used to run GIL tests. Since the `CMAKE_CXX_STANDARD` option in the current -[CMakeLists.txt](CMakeLists.txt) defaults to C++11, pass the default `cxxstd=11` to `b2`: +[CMakeLists.txt](CMakeLists.txt) defaults to C++14, pass the default `cxxstd=14` to `b2`: ```shell ./b2 headers - ./b2 variant=debug,release cxxstd=11 --with-filesystem stage + ./b2 variant=debug,release cxxstd=14 --with-filesystem stage ``` or, depending on specific requirements, more complete build: ```shell - ./b2 variant=debug,release address-model=32,64 cxxstd=11 --layout=versioned --with-filesystem stage + ./b2 variant=debug,release address-model=32,64 cxxstd=14 --layout=versioned --with-filesystem stage ``` If you wish to build tests using different C++ standard version, then adjust the `cxxstd` accordingly. @@ -447,50 +447,52 @@ Here is an example of such lightweight workflow in Linux environment (Debian-bas - Configure build with CMake ```shell - mkdir _build - cd _build/ - cmake .. + cmake -S . -B _build -DBoost_ADDITIONAL_VERSIONS=1.80 -DBoost_COMPILER=-gcc11 -DBoost_ARCHITECTURE=-x64 ``` - **TIP:** By default, tests and [examples](example/README.md) are compiled using - the minimum required C++11. - Specify `-DCMAKE_CXX_STANDARD=14|17|20` to use newer version. + By default, tests and [examples](example/README.md) are compiled using + the minimum required C++14. + Specify `-DCMAKE_CXX_STANDARD=17|20` to use newer version. For more CMake options available for GIL, check `option`-s defined in the top-level `CMakeLists.txt`. - **TIP:** If CMake is failing to find Boost libraries, especially built - with `--layout=versioned`, you can try a few hacks: - - option `-DBoost_ARCHITECTURE=-x64` to help CMake find Boost 1.66 and above - add an architecture tag to the library file names in versioned build - The option added in CMake 3.13.0. + CMake is failing to find Boost libraries, especially built with `--layout=versioned`, + you can try a few hacks as displayed in the command above: - option `-DBoost_COMPILER=-gcc5` or `-DBoost_COMPILER=-vc141` to help CMake earlier than 3.13 match your compiler with toolset used in the Boost library file names (i.e. `libboost_filesystem-gcc5-mt-x64-1_69` and not `-gcc55-`). Fixed in CMake 3.13.0. + - option `-DBoost_ARCHITECTURE=-x64` to help CMake find Boost 1.66 and above + add an architecture tag to the library file names in versioned build + The option added in CMake 3.13.0. + - option `-DDBoost_ADDITIONAL_VERSIONS=` is especially usefull + when testing GIL with staged build of Boost from its `develop` branch. - if CMake is still failing to find Boost, you may try `-DBoost_DEBUG=ON` to get detailed diagnostics output from `FindBoost.cmake` module. - List available CMake targets ```shell - cmake --build . --target help + cmake --build _build --target help ``` - Build selected target with CMake ```shell - cmake --build . --target gil_test_pixel + cmake --build _build --target gil_test_pixel ``` - List available CTest targets ```shell + cd __build ctest --show-only | grep Test ``` - Run selected test with CTest ```shell + cd __build ctest -R gil.tests.core.pixel ``` @@ -567,10 +569,10 @@ Thus, below a few basic guidelines are listed. First and foremost, make sure you are familiar with the official [Boost Library Requirements and Guidelines](https://www.boost.org/development/requirements.html). -Second, strive for writing idiomatic C++11, clean and elegant code. +Second, strive for writing idiomatic C++14, clean and elegant code. **NOTE:** *The Boost.GIL source code does not necessary represent clean and elegant -code to look up to. The library has recently entered the transition to C++11. +code to look up to. The library has recently entered the transition to C++14. Major refactoring overhaul is ongoing.* Maintain structure your source code files according to the following guidelines: diff --git a/README.md b/README.md index 1445b66e8b..581e83dea5 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Documentation | GitHub Actions | AppVeyor | Azure Pipelines | Regression | Codec Boost.GIL is a part of the [Boost C++ Libraries](http://github.com/boostorg). -The Boost Generic Image Library (GIL) is a **C++11** header-only library that abstracts image +The Boost Generic Image Library (GIL) is a **C++14** header-only library that abstracts image representations from algorithms and allows writing code that can work on a variety of images with performance similar to hand-writing for a specific image type. @@ -52,11 +52,9 @@ See [example/cmake/README.md](example/cmake/README.md) for CMake configuration e ## Requirements -**NOTE:** The library source code is currently being modernized for C++11. - The Boost Generic Image Library (GIL) requires: -- C++11 compiler (GCC 4.9, clang 3.3, MSVC++ 14.0 (1900) or any later version) +- C++14 compiler (GCC 6, clang 3.9, MSVC++ 14.1 (1910) or any later version) - Boost header-only libraries Optionally, in order to build and run tests and examples: diff --git a/doc/index.rst b/doc/index.rst index 71bdcf1200..40779419c7 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,7 +1,7 @@ Boost Generic Image Library =========================== -The Generic Image Library (GIL) is a C++11 header-only library that abstracts image +The Generic Image Library (GIL) is a C++14 header-only library that abstracts image representations from algorithms and allows writing code that can work on a variety of images with performance similar to hand-writing for a specific image type. diff --git a/doc/installation.rst b/doc/installation.rst index d810645159..41b910d3cd 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -20,13 +20,11 @@ Compiling --------- The Boost.GIL library source code should successfully compile with any -compiler with complete C++11 support. +compiler with complete C++14 support. .. note:: - We are planning to drop support for require C++14 support in Boost 1.76 or later, - or selectively drop support for GCC 5 due to its issues with inheriting constructors, - see `discussion for PR #526 `_. + Boost.GIL requires C++14 compiler since ince Boost 1.80. For the actual list of currently tested compilers, check results of the library CI builds linked from the `README.md `_ diff --git a/example/adaptive_histogram_equalization.md b/example/adaptive_histogram_equalization.md index 9050af0b76..5737ea4c96 100644 --- a/example/adaptive_histogram_equalization.md +++ b/example/adaptive_histogram_equalization.md @@ -3,6 +3,7 @@ Adaptive Histogram Equalization (AHE) capabilities in GIL are demonstrated by the program `adaptive_histogram_equalization`, compiled from the sources `example/adaptive_histogram_equalization.cpp`. ## Synopsis + `adaptive_histogram_equalization` The program doesn't take any argument on the command line. @@ -12,9 +13,11 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + - `adaptive_histogram_equalization` expects to find an image called `test_adaptive.png` in the current directory. diff --git a/example/adaptive_threshold.md b/example/adaptive_threshold.md index a005307786..a21438c492 100644 --- a/example/adaptive_threshold.md +++ b/example/adaptive_threshold.md @@ -3,6 +3,7 @@ Adaptive Thresholding capabilities in GIL are demonstrated by the program `adaptive_threshold`, compiled from the sources `example/adaptive_threshold.cpp`. ## Synopsis + `adaptive_threshold` The program doesn't take any argument on the command line. @@ -16,8 +17,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + - `adaptive_threshold` expects to find an image called `test_adaptive.png` in the current directory. diff --git a/example/affine.md b/example/affine.md index 9eeeb24a88..00c22494ad 100644 --- a/example/affine.md +++ b/example/affine.md @@ -3,6 +3,7 @@ Affine transformation capabilities in GIL are demonstrated by the program `affine`, compiled from the sources `example/affine.cpp`. ## Synopsis + `affine` The program doesn't take any argument on the command line. @@ -12,8 +13,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + - `affine` expects to find an image called `test.jpg` in the current directory. diff --git a/example/anisotropic_diffusion.md b/example/anisotropic_diffusion.md index 36b942fae8..770fa452c7 100644 --- a/example/anisotropic_diffusion.md +++ b/example/anisotropic_diffusion.md @@ -3,6 +3,7 @@ Anisotropic diffusion capabilities in GIL are demonstrated by the program `anisotropic_diffusion`, compiled from the sources `example/anisotropic_diffusion.cpp`. ## Synopsis + `anisoptropic_diffusion input.png output.png gray|rgb iterations kappa` - The first parameter must be the full path to an existing image in the JPEG format for `anisoptropic_diffusion` to process - The second parameter is the full path to the output image of `anisotropic_diffusion`. The directory will *not* be created and must exist. @@ -15,8 +16,10 @@ Note that both the input and the ouput images must be in the PNG format. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + `anisotropic_diffusion` has no specific execution requirements. diff --git a/example/convolution.md b/example/convolution.md index 70ba03ae59..0e0b621fc1 100644 --- a/example/convolution.md +++ b/example/convolution.md @@ -3,6 +3,7 @@ Convolution capabilities in GIL are demonstrated by the program `convolution`, compiled from the sources `example/convolution.cpp`. ## Synopsis + `convolution` The program doesn't take any argument on the command line. @@ -12,8 +13,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + - `convolution` expects to find an image called `test.jpg` in the current directory. diff --git a/example/convolve2d.md b/example/convolve2d.md index 1027d4d390..86c2bf9351 100644 --- a/example/convolve2d.md +++ b/example/convolve2d.md @@ -3,6 +3,7 @@ 2d kernel convolution capabilities in GIL are demonstrated by the program `convolve2d`, compiled from the sources `example/convolve2d.cpp`. ## Synopsis + `convolve2d` The program doesn't take any argument on the command line. @@ -14,9 +15,11 @@ Note that the user is expected to press a key to end the program. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured - The PNG library installed and configured. ### Execution requirements + - `convolve2d` expects to find an image called `src_view.png` in the current directory. diff --git a/example/dynamic_image.md b/example/dynamic_image.md index 60ff20d172..fb27cfbe46 100644 --- a/example/dynamic_image.md +++ b/example/dynamic_image.md @@ -3,6 +3,7 @@ Dynamic image manipulation capabilities in GIL are demonstrated by the program `dynamic_image`, compiled from the sources `example/dynamic_image.cpp`. ## Synopsis + `dynamic_image` The program doesn't take any argument on the command line. @@ -12,8 +13,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured ### Execution requirements + - `dynamic_image` expects to find an image called `test.jpg` in the current directory. diff --git a/example/harris.md b/example/harris.md index 364abceed5..21770da315 100644 --- a/example/harris.md +++ b/example/harris.md @@ -3,6 +3,7 @@ Harris corner detection capabilities in GIL are demonstrated by the program `harris`, compiled from the sources `example/harris.cpp` and `hvstack.hpp`. ## Synopsis + `harris input.png window-size discriminant harris-threshold output.png` - The first parameter must be the full path to an existing image in the PNG format for `harris` to process @@ -14,8 +15,10 @@ Harris corner detection capabilities in GIL are demonstrated by the program `har ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured ### Execution requirements + - `harris` has no specific execution requirements. diff --git a/example/hessian.md b/example/hessian.md index b759712c9f..376cefdb8c 100644 --- a/example/hessian.md +++ b/example/hessian.md @@ -13,7 +13,7 @@ Hessian feature detection capabilities in GIL are demonstrated by the program `h ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured ### Execution requirements diff --git a/example/histogram.md b/example/histogram.md index 2a33f42856..e785c8741a 100644 --- a/example/histogram.md +++ b/example/histogram.md @@ -3,6 +3,7 @@ Histogram capabilities in GIL are demonstrated by the program `histogram`, compiled from the sources `example/histogram.cpp`. ## Synopsis + `histogram` The program doesn't take any argument on the command line. @@ -13,8 +14,10 @@ The program doesn't produce any output. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + - `histogram` expects to find an image called `test_adaptive.png` in the current directory. diff --git a/example/histogram_equalization.md b/example/histogram_equalization.md index eb71568dac..67a8ee1bec 100644 --- a/example/histogram_equalization.md +++ b/example/histogram_equalization.md @@ -3,6 +3,7 @@ Histogram equalization capabilities in GIL are demonstrated by the program `histogram_equalization`, compiled from the sources `example/histogram_equalization.cpp`. ## Synopsis + `histogram_equalization` The program doesn't take any argument on the command line. @@ -58,8 +59,10 @@ and is a hypotenuse! Using trivial trigonometry we know that the length we are s ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + - `histogram_equalization` expects to find an image called `test_adaptive.png` in the current directory. diff --git a/example/histogram_matching.md b/example/histogram_matching.md index 47f16155dc..9c1e904abb 100644 --- a/example/histogram_matching.md +++ b/example/histogram_matching.md @@ -12,7 +12,7 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements diff --git a/example/hough_transform_circle.md b/example/hough_transform_circle.md index 253912584a..9bcfcd6ff6 100644 --- a/example/hough_transform_circle.md +++ b/example/hough_transform_circle.md @@ -3,6 +3,7 @@ Hough circle transform capabilities in GIL are demonstrated by the program `hough_transform_circle`, compiled from the sources `example/hough_transform_circle.cpp`. ## Synopsis + `hough_transform_circle` The program doesn't take any argument on the command line. @@ -12,7 +13,9 @@ The program outputs the voting cell of the centre of a circle drawn programatica ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above. + +- A C++ compiler compliant with C++14 or above. ### Execution requirements + `hough_transform_circle` doesn't have any specific execution requirements. diff --git a/example/hough_transform_line.md b/example/hough_transform_line.md index 9fd0171b9f..a49493ecf4 100644 --- a/example/hough_transform_line.md +++ b/example/hough_transform_line.md @@ -3,6 +3,7 @@ Hough line transform capabilities in GIL are demonstrated by the program `hough_transform_line`, compiled from the sources `example/hough_transform_line.cpp`. ## Synopsis + `hough_transform_line` The program doesn't take any argument on the command line. @@ -12,7 +13,9 @@ The program outputs the expected theta and radius, followed by every step of the ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above. + +- A C++ compiler compliant with C++14 or above. ### Execution requirements + `hough_transform_line` doesn't have any specific execution requirements. diff --git a/example/interleaved_ptr.md b/example/interleaved_ptr.md index 0575492e94..c0e3727711 100644 --- a/example/interleaved_ptr.md +++ b/example/interleaved_ptr.md @@ -3,6 +3,7 @@ Definition of custom pixel reference and iterator capabilities in GIL are demonstrated by the program `interleaved_ptr`, compiled from the sources `example/interleaved_ptr.cpp`, `interleaved_ptr.hpp` and `interleaved_ref.hpp`. ## Synopsis + `interleaved_ptr` The program doesn't take any argument on the command line. @@ -12,8 +13,10 @@ The program expects to find an image, `test.jpg` in the current directory, and p ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + `interleaved_ptr` expects to find an image, `test.jpg` in the current directory. diff --git a/example/mandelbrot.md b/example/mandelbrot.md index 07d24b8e34..05af4a46ad 100644 --- a/example/mandelbrot.md +++ b/example/mandelbrot.md @@ -3,6 +3,7 @@ Pixel iteration using `virtual_2d_locators` capabilities in GIL are demonstrated by the program `mandelbrot`, compiled from the sources `example/mandelbrot.cpp`. ## Synopsis + `mandelbrot` The program doesn't take any argument on the command line. @@ -12,8 +13,10 @@ The program produces the image `out-mandelbrot.jpg` that has been generated usin ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + `mandelbrot` has no specific execution requirements. diff --git a/example/morphology.md b/example/morphology.md index 7ebb0f584d..b7cbfa5b8a 100644 --- a/example/morphology.md +++ b/example/morphology.md @@ -3,6 +3,7 @@ Morphological operations capabilities in GIL are demonstrated by the program `morphology`, compiled from the sources `example/morphology.cpp`. ## Synopsis + `morphology input.png output-image-template operation1 [operation2 ... operationN]` - The first parameter must be the full path to an existing image in the PNG format for `morphology` to process @@ -24,8 +25,10 @@ The operations can be provided in any order, only note that if `binary` is suppl ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + `morphology` has no specific execution requirements. diff --git a/example/packed_pixel.md b/example/packed_pixel.md index 3792ced288..be8a759176 100644 --- a/example/packed_pixel.md +++ b/example/packed_pixel.md @@ -3,6 +3,7 @@ Packed pixel formats capabilities in GIL are demonstrated by the program `packed_pixel`, compiled from the sources `example/packed_pixel.cpp`. ## Synopsis + `packed_pixel` The program doesn't take any argument on the command line. @@ -12,8 +13,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + `packed_pixel` expects to find an image called `input.jpg` in the current directory in JPEG format. diff --git a/example/rasterizer_circle.md b/example/rasterizer_circle.md index bf435d8c43..9f2dafd562 100644 --- a/example/rasterizer_circle.md +++ b/example/rasterizer_circle.md @@ -3,6 +3,7 @@ Circle rasterization capabilities in GIL are demonstrated by the program `rasterizer_circle`, compiled from the sources `example/rasterizer_circle.cpp`. ## Synopsis + `rasterizer_circle` The program doesn't take any argument on the command line. @@ -12,8 +13,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + `rasterizer_circle` doesn't have any specific execution requirements. diff --git a/example/rasterizer_ellipse.md b/example/rasterizer_ellipse.md index 8851e30d99..345219bc7c 100644 --- a/example/rasterizer_ellipse.md +++ b/example/rasterizer_ellipse.md @@ -3,6 +3,7 @@ Ellipse rasterization capabilities in GIL are demonstrated by the program `rasterizer_ellipse`, compiled from the sources `example/rasterizer_ellipse.cpp`. ## Synopsis + `rasterizer_ellipse` The program doesn't take any argument on the command line. @@ -15,8 +16,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + `rasterizer_ellipse` doesn't have any specific execution requirements. diff --git a/example/rasterizer_line.md b/example/rasterizer_line.md index 5ff366b305..849a297744 100644 --- a/example/rasterizer_line.md +++ b/example/rasterizer_line.md @@ -3,6 +3,7 @@ Line rasterization capabilities in GIL are demonstrated by the program `rasterizer_line`, compiled from the sources `example/rasterizer_line.cpp`. ## Synopsis + `rasterizer_line` The program doesn't take any argument on the command line. @@ -16,8 +17,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + `rasterizer_line` doesn't have any specific execution requirements. diff --git a/example/resize.md b/example/resize.md index 7d5c5a1781..dd6d787cb8 100644 --- a/example/resize.md +++ b/example/resize.md @@ -3,6 +3,7 @@ Resizing capabilities in GIL are demonstrated by the program `resize`, compiled from the sources `example/resize.cpp`. ## Synopsis + `resize` This program expects to find an image called `test.jpg`, in JPEG format, in the current directory, and produces a scaled down version of this image, called `out-resize.jpg`, in JPEG format in the current directory. @@ -12,8 +13,10 @@ The program doesn't take any argument on the command line. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + `resize` expects to find an image called `test.jpg`, in JPEG format, in the current directory. diff --git a/example/sobel_scharr.md b/example/sobel_scharr.md index 73510f2fc5..416b67caba 100644 --- a/example/sobel_scharr.md +++ b/example/sobel_scharr.md @@ -3,6 +3,7 @@ Edge detection via Sobel and Scharr filters capabilities in GIL are demonstrated by the program `sobel_scharr`, compiled from the sources `example/sobel_scharr.cpp`. ## Synopsis + `sobel_scharr input.png sobel|scharr output-x.png output-y.png` - The first argument must be the full path to an existing image in PNG format @@ -13,8 +14,10 @@ Edge detection via Sobel and Scharr filters capabilities in GIL are demonstrated ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The PNG library installed and configured. ### Execution requirements + `sobek_scharr` doesn't have any specific execution requirements. diff --git a/example/threshold.md b/example/threshold.md index 79289637ef..3c09656194 100644 --- a/example/threshold.md +++ b/example/threshold.md @@ -3,6 +3,7 @@ Thresholding capabilities in GIL are demonstrated by the program `threshold`, compiled from the sources `example/threshold.cpp`. ## Synopsis + `threshold` The program expects to find an image in the JPEG format in the current directory named `input.jpg`, and produces two images in JPEG format in the current directory in return, named `out-threshold-binary.jpg` and `out-threshold-binary-inv.jpg`, where a thresholding and an inverse thresholding, respectively, has been applied. @@ -12,8 +13,10 @@ The program doesn't take any command line arguments. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + `threshold` expects to find an image in the JPEG format in the current directory. diff --git a/example/tutorial_histogram.md b/example/tutorial_histogram.md index bb41d7ebe0..5d5e44c73a 100644 --- a/example/tutorial_histogram.md +++ b/example/tutorial_histogram.md @@ -3,6 +3,7 @@ Histogram computation capabilities in GIL are demonstrated by the program `tutorial_histogram`, compiled from the sources `example/tutorial_histogram.cpp`. ## Synopsis + `tutorial_histogram` The program expects to find an image in the JPEG format in the current directory named `input.jpg`, and produces a text file named `out-histogram.txt`, in the current direction, where the histogram has been calculated.. @@ -12,8 +13,10 @@ The program doesn't take any command line arguments. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + `tutorial_histogram` expects to find an image in the JPEG format in the current directory. diff --git a/example/x_gradient.md b/example/x_gradient.md index ab273dea22..c1e4500df4 100644 --- a/example/x_gradient.md +++ b/example/x_gradient.md @@ -3,6 +3,7 @@ Gradients calculations capabilities in GIL are demonstrated by the program `x_gradient`, compiled from the sources `example/x_gradient.cpp`. ## Synopsis + `x_gradient` The program expects to find an image in the JPEG format in the current directory named `input.jpg`, and produces an image in JPEG format in the current directory in return, named `out-x_gradient.jpg`, where the gradients have been captured. @@ -12,8 +13,10 @@ The program doesn't take any command line arguments. ## Specific requirements ### Build requirements -- A C++ compiler compliant with C++11 or above + +- A C++ compiler compliant with C++14 or above - The JPEG library installed and configured. ### Execution requirements + `x_gradient` expects to find an image in the JPEG format in the current directory. From 8d7034c71cdb3ea1e946f57ca3b30e81b8a8b854 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Mon, 27 Jun 2022 22:59:52 +0200 Subject: [PATCH 145/193] fix: Memory leak in image class for empty dimensions (#649) --- include/boost/gil/image.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/boost/gil/image.hpp b/include/boost/gil/image.hpp index d6a321bd16..e3f943a839 100644 --- a/include/boost/gil/image.hpp +++ b/include/boost/gil/image.hpp @@ -449,6 +449,11 @@ class image { // if it throws and _memory!=0 the client must deallocate _memory _allocated_bytes = total_allocated_size_in_bytes(dimensions); + if (_allocated_bytes == 0) + { + return; + } + _memory=_alloc.allocate( _allocated_bytes ); unsigned char* tmp=(_align_in_bytes>0) ? (unsigned char*)align((std::size_t)_memory,_align_in_bytes) : _memory; @@ -465,6 +470,10 @@ class image std::size_t plane_size=row_size*dimensions.y; _allocated_bytes = total_allocated_size_in_bytes( dimensions ); + if (_allocated_bytes == 0) + { + return; + } _memory = _alloc.allocate( _allocated_bytes ); From a0ac9fb4aa04250698cda957ea8f34e0efb0ff73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 28 Jun 2022 00:34:44 +0200 Subject: [PATCH 146/193] test: Case test_constructor_from_view was not called --- test/core/image/image.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/core/image/image.cpp b/test/core/image/image.cpp index 1ab02f1fda..b2cca68688 100644 --- a/test/core/image/image.cpp +++ b/test/core/image/image.cpp @@ -152,6 +152,7 @@ int main() { test_constructor_with_dimensions_pixel::run(); test_constructor_from_other_image::run(); + test_constructor_from_view::run(); test_move_constructor::run(); test_move_assignement::run(); From ef9b89a93a526fc091479b6ece107c89e7557809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 28 Jun 2022 06:59:33 +0100 Subject: [PATCH 147/193] test: Add test cases for image with empty dimensions (#702) Test fix #649 --- test/core/image/CMakeLists.txt | 3 +- test/core/image/Jamfile | 1 + test/core/image/empty_dimensions.cpp | 91 ++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 test/core/image/empty_dimensions.cpp diff --git a/test/core/image/CMakeLists.txt b/test/core/image/CMakeLists.txt index 6574cf2757..e121f3f9bf 100644 --- a/test/core/image/CMakeLists.txt +++ b/test/core/image/CMakeLists.txt @@ -7,7 +7,8 @@ # foreach(_name concepts - image) + image + empty_dimensions) set(_test t_core_image_${_name}) set(_target test_core_image_${_name}) diff --git a/test/core/image/Jamfile b/test/core/image/Jamfile index 07da63467d..e32d74da8b 100644 --- a/test/core/image/Jamfile +++ b/test/core/image/Jamfile @@ -11,3 +11,4 @@ import testing ; compile concepts.cpp ; run image.cpp ; +run empty_dimensions.cpp ; diff --git a/test/core/image/empty_dimensions.cpp b/test/core/image/empty_dimensions.cpp new file mode 100644 index 0000000000..9a665e490f --- /dev/null +++ b/test/core/image/empty_dimensions.cpp @@ -0,0 +1,91 @@ +// +// Copyright 2019-2020 Mateusz Loskot +// +// Distributed under 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 "test_fixture.hpp" +#include "test_utility_output_stream.hpp" +#include "core/pixel/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +// Test cases of memory leak potential for image created with empty dimesions, +// see https://github.com/boostorg/gil/pull/649 +// The main goal of these test cases is to trigger any memory leak detectors. + +void test_default_constructor() +{ + boost::gil::rgb8_image_t image; + BOOST_TEST_EQ(image.width(), 0); + BOOST_TEST_EQ(image.height(), 0); +} + +void test_copy_constructor_with_empty_image() +{ + boost::gil::rgb8_image_t image1; + boost::gil::rgb8_image_t image2(image1); + BOOST_TEST_EQ(image2.width(), 0); + BOOST_TEST_EQ(image2.height(), 0); +} + +struct test_constructor_with_empty_dimensions +{ + template + void operator()(Image const &) + { + using image_t = Image; + image_t image(0, 0); + BOOST_TEST_EQ(image.width(), 0); + BOOST_TEST_EQ(image.height(), 0); + } + + static void run() + { + boost::mp11::mp_for_each(test_constructor_with_empty_dimensions{}); + } +}; + +struct test_constructor_with_empty_dimensions_with_pixel +{ + template + void operator()(Image const &) + { + using image_t = Image; + gil::point_t const dimensions{0, 0}; + using pixel_t = typename image_t::view_t::value_type; + pixel_t const rnd_pixel = fixture::pixel_generator::random(); + image_t image(dimensions, rnd_pixel); + BOOST_TEST_EQ(image.width(), dimensions.x); + BOOST_TEST_EQ(image.height(), dimensions.y); + + bool none_visited = true; + for (pixel_t const &p : gil::view(image)) + { + none_visited = false; + BOOST_TEST_EQ(p, rnd_pixel); + } + BOOST_TEST(none_visited); + } + + static void run() + { + boost::mp11::mp_for_each(test_constructor_with_empty_dimensions_with_pixel{}); + } +}; + +int main() +{ + test_default_constructor(); + test_copy_constructor_with_empty_image(); + test_constructor_with_empty_dimensions::run(); + test_constructor_with_empty_dimensions_with_pixel::run(); + + return ::boost::report_errors(); +} From 4dbf35a510fd2bad2ed8c59c4dd523de4b3cf77b Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Tue, 28 Jun 2022 08:35:27 +0200 Subject: [PATCH 148/193] Add pmr image typedefs (#529) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add pmr image typedefs * Swap allocators only if it propagate on container * Do not call propagate_on_container_swap for C++14 Co-authored-by: Mateusz Łoskot --- include/boost/gil/image.hpp | 7 ++ include/boost/gil/typedefs.hpp | 147 ++++++++++++++++++------------- test/core/image/image.cpp | 29 +++++- test/core/image/test_fixture.hpp | 20 +++++ 4 files changed, 143 insertions(+), 60 deletions(-) diff --git a/include/boost/gil/image.hpp b/include/boost/gil/image.hpp index e3f943a839..86675b97d5 100644 --- a/include/boost/gil/image.hpp +++ b/include/boost/gil/image.hpp @@ -238,7 +238,14 @@ class image swap(_align_in_bytes, img._align_in_bytes); swap(_memory, img._memory); swap(_view, img._view); +#ifdef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE swap(_alloc, img._alloc); +#else + if constexpr (std::allocator_traits::propagate_on_container_swap::value) + swap(_alloc, img._alloc); + else + BOOST_ASSERT(_alloc == img._alloc); +#endif swap(_allocated_bytes, img._allocated_bytes ); } diff --git a/include/boost/gil/typedefs.hpp b/include/boost/gil/typedefs.hpp index 4ff10d52d7..44673954ce 100644 --- a/include/boost/gil/typedefs.hpp +++ b/include/boost/gil/typedefs.hpp @@ -18,71 +18,100 @@ #include #include +#if !defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE) +# include +#endif //!defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE) // B - bits size/signedness, CM - channel model, CS - colour space, LAYOUT - pixel layout // Example: B = '8', CM = 'uint8_t', CS = 'bgr, LAYOUT='bgr_layout_t' -#define BOOST_GIL_DEFINE_BASE_TYPEDEFS_INTERNAL(B, CM, CS, LAYOUT) \ - template struct pixel; \ - template struct planar_pixel_reference; \ - template struct planar_pixel_iterator; \ - template class memory_based_step_iterator; \ - template class point; \ - template class memory_based_2d_locator; \ - template class image_view; \ - template class image; \ - using CS##B##_pixel_t = pixel; \ - using CS##B##c_pixel_t = pixel const; \ - using CS##B##_ref_t = pixel&; \ - using CS##B##c_ref_t = pixel const&; \ - using CS##B##_ptr_t = CS##B##_pixel_t*; \ - using CS##B##c_ptr_t = CS##B##c_pixel_t*; \ - using CS##B##_step_ptr_t = memory_based_step_iterator; \ - using CS##B##c_step_ptr_t = memory_based_step_iterator; \ - using CS##B##_loc_t \ - = memory_based_2d_locator>; \ - using CS##B##c_loc_t \ - = memory_based_2d_locator>; \ - using CS##B##_step_loc_t \ - = memory_based_2d_locator>; \ - using CS##B##c_step_loc_t \ - = memory_based_2d_locator>; \ - using CS##B##_view_t = image_view; \ - using CS##B##c_view_t = image_view; \ - using CS##B##_step_view_t = image_view; \ - using CS##B##c_step_view_t = image_view; \ +#define BOOST_GIL_DEFINE_BASE_TYPEDEFS_INTERNAL(B, CM, CS, LAYOUT) \ + template \ + struct pixel; \ + template \ + struct planar_pixel_reference; \ + template \ + struct planar_pixel_iterator; \ + template \ + class memory_based_step_iterator; \ + template \ + class point; \ + template \ + class memory_based_2d_locator; \ + template \ + class image_view; \ + template \ + class image; \ + using CS##B##_pixel_t = pixel; \ + using CS##B##c_pixel_t = pixel const; \ + using CS##B##_ref_t = pixel&; \ + using CS##B##c_ref_t = pixel const&; \ + using CS##B##_ptr_t = CS##B##_pixel_t*; \ + using CS##B##c_ptr_t = CS##B##c_pixel_t*; \ + using CS##B##_step_ptr_t = memory_based_step_iterator; \ + using CS##B##c_step_ptr_t = memory_based_step_iterator; \ + using CS##B##_loc_t = memory_based_2d_locator>; \ + using CS##B##c_loc_t = memory_based_2d_locator>; \ + using CS##B##_step_loc_t \ + = memory_based_2d_locator>; \ + using CS##B##c_step_loc_t \ + = memory_based_2d_locator>; \ + using CS##B##_view_t = image_view; \ + using CS##B##c_view_t = image_view; \ + using CS##B##_step_view_t = image_view; \ + using CS##B##c_step_view_t = image_view; \ using CS##B##_image_t = image>; -// Example: B = '8', CM = 'uint8_t', CS = 'bgr' CS_FULL = 'rgb_t' LAYOUT='bgr_layout_t' -#define BOOST_GIL_DEFINE_ALL_TYPEDEFS_INTERNAL(B, CM, CS, CS_FULL, LAYOUT) \ - BOOST_GIL_DEFINE_BASE_TYPEDEFS_INTERNAL(B, CM, CS, LAYOUT) \ - using CS##B##_planar_ref_t = planar_pixel_reference; \ - using CS##B##c_planar_ref_t = planar_pixel_reference; \ - using CS##B##_planar_ptr_t = planar_pixel_iterator; \ - using CS##B##c_planar_ptr_t = planar_pixel_iterator; \ - using CS##B##_planar_step_ptr_t = memory_based_step_iterator; \ - using CS##B##c_planar_step_ptr_t \ - = memory_based_step_iterator; \ - using CS##B##_planar_loc_t \ - = memory_based_2d_locator>; \ - using CS##B##c_planar_loc_t \ - = memory_based_2d_locator>; \ - using CS##B##_planar_step_loc_t \ - = memory_based_2d_locator>; \ - using CS##B##c_planar_step_loc_t \ - = memory_based_2d_locator>; \ - using CS##B##_planar_view_t = image_view; \ - using CS##B##c_planar_view_t = image_view; \ - using CS##B##_planar_step_view_t = image_view; \ - using CS##B##c_planar_step_view_t = image_view; \ - using CS##B##_planar_image_t \ - = image>; - -#define BOOST_GIL_DEFINE_BASE_TYPEDEFS(B, CM, CS) \ - BOOST_GIL_DEFINE_BASE_TYPEDEFS_INTERNAL(B, CM, CS, CS##_layout_t) - -#define BOOST_GIL_DEFINE_ALL_TYPEDEFS(B, CM, CS) \ - BOOST_GIL_DEFINE_ALL_TYPEDEFS_INTERNAL(B, CM, CS, CS##_t, CS##_layout_t) +#define BOOST_GIL_DEFINE_BASE_PMR_TYPEDEFS_INTERNAL(B, CM, CS, LAYOUT) \ + namespace pmr { \ + using CS##B##_image_t \ + = image>; \ + } +// Example: B = '8', CM = 'uint8_t', CS = 'bgr' CS_FULL = 'rgb_t' LAYOUT='bgr_layout_t' +#define BOOST_GIL_DEFINE_ALL_TYPEDEFS_INTERNAL(B, CM, CS, CS_FULL, LAYOUT) \ + BOOST_GIL_DEFINE_BASE_TYPEDEFS_INTERNAL(B, CM, CS, LAYOUT) \ + using CS##B##_planar_ref_t = planar_pixel_reference; \ + using CS##B##c_planar_ref_t = planar_pixel_reference; \ + using CS##B##_planar_ptr_t = planar_pixel_iterator; \ + using CS##B##c_planar_ptr_t = planar_pixel_iterator; \ + using CS##B##_planar_step_ptr_t = memory_based_step_iterator; \ + using CS##B##c_planar_step_ptr_t = memory_based_step_iterator; \ + using CS##B##_planar_loc_t \ + = memory_based_2d_locator>; \ + using CS##B##c_planar_loc_t \ + = memory_based_2d_locator>; \ + using CS##B##_planar_step_loc_t \ + = memory_based_2d_locator>; \ + using CS##B##c_planar_step_loc_t \ + = memory_based_2d_locator>; \ + using CS##B##_planar_view_t = image_view; \ + using CS##B##c_planar_view_t = image_view; \ + using CS##B##_planar_step_view_t = image_view; \ + using CS##B##c_planar_step_view_t = image_view; \ + using CS##B##_planar_image_t = image>; + +#define BOOST_GIL_DEFINE_ALL_PMR_TYPEDEFS_INTERNAL(B, CM, CS, CS_FULL, LAYOUT) \ + BOOST_GIL_DEFINE_BASE_PMR_TYPEDEFS_INTERNAL(B, CM, CS, LAYOUT) \ + namespace pmr { \ + using CS##B##_planar_image_t \ + = image>; \ + } + +#if defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE) +# define BOOST_GIL_DEFINE_BASE_TYPEDEFS(B, CM, CS) \ + BOOST_GIL_DEFINE_BASE_TYPEDEFS_INTERNAL(B, CM, CS, CS##_layout_t) + +# define BOOST_GIL_DEFINE_ALL_TYPEDEFS(B, CM, CS) \ + BOOST_GIL_DEFINE_ALL_TYPEDEFS_INTERNAL(B, CM, CS, CS##_t, CS##_layout_t) +#else +# define BOOST_GIL_DEFINE_BASE_TYPEDEFS(B, CM, CS) \ + BOOST_GIL_DEFINE_BASE_TYPEDEFS_INTERNAL(B, CM, CS, CS##_layout_t) \ + BOOST_GIL_DEFINE_BASE_PMR_TYPEDEFS_INTERNAL(B, CM, CS, CS##_layout_t) + +# define BOOST_GIL_DEFINE_ALL_TYPEDEFS(B, CM, CS) \ + BOOST_GIL_DEFINE_ALL_TYPEDEFS_INTERNAL(B, CM, CS, CS##_t, CS##_layout_t) \ + BOOST_GIL_DEFINE_ALL_PMR_TYPEDEFS_INTERNAL(B, CM, CS, CS##_t, CS##_layout_t) +#endif //!defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE) namespace boost { namespace gil { diff --git a/test/core/image/image.cpp b/test/core/image/image.cpp index b2cca68688..cf73f1c7db 100644 --- a/test/core/image/image.cpp +++ b/test/core/image/image.cpp @@ -35,6 +35,7 @@ struct test_constructor_with_dimensions_pixel static void run() { boost::mp11::mp_for_each(test_constructor_with_dimensions_pixel{}); + boost::mp11::mp_for_each(test_constructor_with_dimensions_pixel{}); } }; @@ -44,12 +45,13 @@ struct test_constructor_from_other_image void operator()(Image const &) { using image_t = Image; + using allocator_t = typename Image::allocator_type; gil::point_t const dimensions{256, 128}; using pixel_t = typename image_t::view_t::value_type; pixel_t const rnd_pixel = fixture::pixel_generator::random(); { //constructor interleaved from planar - gil::image image1(dimensions, rnd_pixel); + gil::image image1(dimensions, rnd_pixel); image_t image2(image1); BOOST_TEST_EQ(image2.dimensions(), dimensions); auto v1 = gil::const_view(image1); @@ -106,6 +108,27 @@ struct test_constructor_from_view } }; +struct test_copy_assignement +{ + template + void operator()(Image const&) + { + using image_t = Image; + gil::point_t const dimensions{ 256, 128 }; + { + image_t image = fixture::create_image(dimensions.x, dimensions.y, 0); + image_t image2; + image2 = image; + BOOST_TEST_EQ(image2.dimensions(), dimensions); + } + } + static void run() + { + boost::mp11::mp_for_each(test_copy_assignement{}); + boost::mp11::mp_for_each(test_copy_assignement{}); + } +}; + struct test_move_constructor { template @@ -124,6 +147,7 @@ struct test_move_constructor static void run() { boost::mp11::mp_for_each(test_move_constructor{}); + boost::mp11::mp_for_each(test_move_constructor{}); } }; @@ -145,6 +169,7 @@ struct test_move_assignement static void run() { boost::mp11::mp_for_each(test_move_assignement{}); + boost::mp11::mp_for_each(test_move_assignement{}); } }; @@ -154,6 +179,8 @@ int main() test_constructor_from_other_image::run(); test_constructor_from_view::run(); + test_copy_assignement::run(); + test_move_constructor::run(); test_move_assignement::run(); diff --git a/test/core/image/test_fixture.hpp b/test/core/image/test_fixture.hpp index 8b149cd630..0a7fc4d865 100644 --- a/test/core/image/test_fixture.hpp +++ b/test/core/image/test_fixture.hpp @@ -38,6 +38,26 @@ using image_types = std::tuple gil::rgba32_image_t >; +#if defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE) + using pmr_image_types = std::tuple<>; +#else + using pmr_image_types = std::tuple + < + gil::pmr::gray8_image_t, + gil::pmr::gray16_image_t, + gil::pmr::gray32_image_t, + gil::pmr::bgr8_image_t, + gil::pmr::bgr16_image_t, + gil::pmr::bgr32_image_t, + gil::pmr::rgb8_image_t, + gil::pmr::rgb16_image_t, + gil::pmr::rgb32_image_t, + gil::pmr::rgba8_image_t, + gil::pmr::rgba16_image_t, + gil::pmr::rgba32_image_t + >; +#endif //defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE) + using rgb_interleaved_image_types = std::tuple < gil::bgr8_image_t, From 8101595cebd5c1c7805600dab021caef471724e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 29 Jun 2022 00:13:44 +0200 Subject: [PATCH 149/193] chore: Update CMake to use latest cmake-conan/0.18.1 [ci skip] --- CMakeLists.txt | 2 +- conanfile.txt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1af6bf85c1..fd94577e89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,7 +161,7 @@ if(BOOST_GIL_USE_CONAN) # Download automatically, you can also just copy the conan.cmake file if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") message(STATUS "Boost.GIL: Downloading conan.cmake from https://github.com/conan-io/cmake-conan") - file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/v0.15/conan.cmake" + file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/0.18.1/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake") endif() diff --git a/conanfile.txt b/conanfile.txt index 94558d7b19..90916e500e 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2021 Mateusz Loskot +# Copyright (c) 2018-2022 Mateusz Loskot # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at @@ -11,4 +11,5 @@ libpng/1.6.37 libtiff/4.1.0 [generators] -cmake +CMakeDeps +CMakeToolchain From 00423cca03e0641ba3a82f6b85eacac566ecf049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 29 Jun 2022 00:14:28 +0200 Subject: [PATCH 150/193] chore: Update CMakeSettings.json sample [ci skip] --- example/cmake/CMakeSettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/cmake/CMakeSettings.json b/example/cmake/CMakeSettings.json index 8aee0eeaa4..c28101f929 100644 --- a/example/cmake/CMakeSettings.json +++ b/example/cmake/CMakeSettings.json @@ -7,8 +7,8 @@ { "BuildDir": "${workspaceRoot}\\_build" }, { "InstallDir": "${workspaceRoot}\\_install" }, { "DEFAULT_BUILD_TESTING": "OFF" }, - { "DEFAULT_Boost_ADDITIONAL_VERSIONS": "1.74;1.73;1.72;1.71" }, - { "DEFAULT_Boost_COMPILER": "-vc142;-vc141" }, + { "DEFAULT_Boost_ADDITIONAL_VERSIONS": "1.80" }, + { "DEFAULT_Boost_COMPILER": "-vc143" }, { "DEFAULT_Boost_DEBUG": "ON" }, { "DEFAULT_GIL_BUILD_EXAMPLES": "ON" }, { "DEFAULT_GIL_BUILD_HEADER_TESTS": "OFF" }, From 08e3e6dadb4e344757802b668def8f1bab7952e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 29 Jun 2022 01:00:14 +0200 Subject: [PATCH 151/193] test: Add basic is_1d_traversable cases for image_view --- test/core/image_view/CMakeLists.txt | 1 + test/core/image_view/Jamfile | 1 + test/core/image_view/is_1d_traversable.cpp | 67 ++++++++++++++++++++++ test/core/image_view/planar_rgba_view.cpp | 9 +++ 4 files changed, 78 insertions(+) create mode 100644 test/core/image_view/is_1d_traversable.cpp diff --git a/test/core/image_view/CMakeLists.txt b/test/core/image_view/CMakeLists.txt index e9983da942..5df96c4227 100644 --- a/test/core/image_view/CMakeLists.txt +++ b/test/core/image_view/CMakeLists.txt @@ -12,6 +12,7 @@ foreach(_name derived_view_type dynamic_step is_planar + is_1d_traversable iterator planar_rgba_view reverse_iterator diff --git a/test/core/image_view/Jamfile b/test/core/image_view/Jamfile index 4b654b92cb..03aecea5a7 100644 --- a/test/core/image_view/Jamfile +++ b/test/core/image_view/Jamfile @@ -20,6 +20,7 @@ compile view_type_from_pixel.cpp ; run axis_iterator.cpp ; run collection.cpp ; +run is_1d_traversable.cpp ; run iterator.cpp ; run planar_rgba_view.cpp ; run reverse_iterator.cpp ; diff --git a/test/core/image_view/is_1d_traversable.cpp b/test/core/image_view/is_1d_traversable.cpp new file mode 100644 index 0000000000..bc0cdda62c --- /dev/null +++ b/test/core/image_view/is_1d_traversable.cpp @@ -0,0 +1,67 @@ +// +// Copyright 2022 Mateusz Loskot +// +// Distributed under 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 "core/image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +// See also test_is_1d_traversable in planar_rgba_view.cpp + +struct test_continuous_image_types +{ + template + void operator()(Image const&) + { + gil::point_t const dimensions{ 8, 3 }; + Image image(dimensions); + auto& view = gil::view(image); + BOOST_TEST(view.is_1d_traversable()); + } + + static void run() + { + boost::mp11::mp_for_each(test_continuous_image_types{}); + } +}; + +struct test_interleaved_image_types +{ + template + void operator()(Image const&) + { + gil::point_t const dimensions{ 8, 3 }; + Image image(dimensions); + auto& view = gil::view(image); + BOOST_TEST(view.is_1d_traversable()); + + // planar copy from interleaved + { + using pixel_t = typename Image::view_t::value_type; + gil::image image2(image); + auto& view2 = gil::view(image2); + BOOST_TEST(view2.is_1d_traversable()); + } + } + + static void run() + { + boost::mp11::mp_for_each(test_interleaved_image_types{}); + } +}; + +int main() +{ + test_continuous_image_types::run(); + test_interleaved_image_types::run(); + + return ::boost::report_errors(); +} diff --git a/test/core/image_view/planar_rgba_view.cpp b/test/core/image_view/planar_rgba_view.cpp index 925a2ebec3..9b32277c62 100644 --- a/test/core/image_view/planar_rgba_view.cpp +++ b/test/core/image_view/planar_rgba_view.cpp @@ -50,6 +50,14 @@ void test_back() BOOST_TEST_EQ(v.back(), pb); } +void test_is_1d_traversable() +{ + auto v2x2 = gil::planar_rgba_view(fixture::d.x, fixture::d.y, fixture::r, fixture::g, fixture::b, fixture::a, sizeof(std::uint8_t) * 2); + BOOST_TEST(v2x2.is_1d_traversable()); + auto v3x1 = gil::planar_rgba_view(3, 1, fixture::r, fixture::g, fixture::b, fixture::a, sizeof(std::uint8_t) * 3); + BOOST_TEST(v3x1.is_1d_traversable()); +} + void test_pixel_equal_to_operator() { auto v = gil::planar_rgba_view(fixture::d.x, fixture::d.y, fixture::r, fixture::g, fixture::b, fixture::a, sizeof(std::uint8_t) * 2); @@ -65,6 +73,7 @@ int main() test_dimensions(); test_front(); test_back(); + test_is_1d_traversable(); test_pixel_equal_to_operator(); return ::boost::report_errors(); From 7b7c786c1b0d253dfbdd460695323311b19e50df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 29 Jun 2022 21:56:30 +0200 Subject: [PATCH 152/193] test: Check more properties of indexed_image_view from extension/toolbox --- test/extension/toolbox/indexed_image.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/extension/toolbox/indexed_image.cpp b/test/extension/toolbox/indexed_image.cpp index 5e78fa23f4..d21ce63938 100644 --- a/test/extension/toolbox/indexed_image.cpp +++ b/test/extension/toolbox/indexed_image.cpp @@ -143,6 +143,16 @@ void test_index_image_view() BOOST_TEST_EQ(gil::get_color(q, gil::red_t()), 70); BOOST_TEST_EQ(gil::get_color(q, gil::green_t()), 80); BOOST_TEST_EQ(gil::get_color(q, gil::blue_t()), 90); + + BOOST_TEST(ii_view.num_dimensions == 2); + BOOST_TEST(ii_view.dimensions().x == width); + BOOST_TEST(ii_view.dimensions().y == height); + BOOST_TEST(ii_view.width() == width); + BOOST_TEST(ii_view.height() == height); + BOOST_TEST(ii_view.num_channels() == gil::num_channels::value); + BOOST_TEST(ii_view.num_colors() == palette_view.dimensions().x); + BOOST_TEST(!ii_view.empty()); + BOOST_TEST(!ii_view.is_1d_traversable()); // by virtual_2d_locator } int main() From 86ee473f51cfc6b2f9ee205bc88b09673fea34b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 29 Jun 2022 22:58:00 +0200 Subject: [PATCH 153/193] test: Add virtual_2d_locator fixture; is_2d_traversable test case --- test/core/CMakeLists.txt | 3 +- test/core/virtual_2d_locator/CMakeLists.txt | 26 +++++++ test/core/virtual_2d_locator/Jamfile | 11 +++ .../virtual_2d_locator/is_2d_traversable.cpp | 41 ++++++++++ test/core/virtual_2d_locator/test_fixture.hpp | 76 +++++++++++++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 test/core/virtual_2d_locator/CMakeLists.txt create mode 100644 test/core/virtual_2d_locator/Jamfile create mode 100644 test/core/virtual_2d_locator/is_2d_traversable.cpp create mode 100644 test/core/virtual_2d_locator/test_fixture.hpp diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 761632decd..2b70482aa0 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -35,8 +35,9 @@ add_subdirectory(color_base) add_subdirectory(pixel) add_subdirectory(iterator) add_subdirectory(locator) +add_subdirectory(virtual_2d_locator) add_subdirectory(image) add_subdirectory(image_view) add_subdirectory(algorithm) add_subdirectory(image_processing) -add_subdirectory(histogram) \ No newline at end of file +add_subdirectory(histogram) diff --git a/test/core/virtual_2d_locator/CMakeLists.txt b/test/core/virtual_2d_locator/CMakeLists.txt new file mode 100644 index 0000000000..fa3ddb8936 --- /dev/null +++ b/test/core/virtual_2d_locator/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# Copyright (c) 2022 Mateusz Loskot +# +# Distributed under 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) +# +foreach(_name + is_2d_traversable) + set(_test t_core_virtual_2d_locator_${_name}) + set(_target test_core_virtual_2d_locator_${_name}) + + add_executable(${_target} "") + target_sources(${_target} PRIVATE ${_name}.cpp) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK) + add_test(NAME ${_test} COMMAND ${_target}) + + unset(_name) + unset(_target) + unset(_test) +endforeach() diff --git a/test/core/virtual_2d_locator/Jamfile b/test/core/virtual_2d_locator/Jamfile new file mode 100644 index 0000000000..5607cd3741 --- /dev/null +++ b/test/core/virtual_2d_locator/Jamfile @@ -0,0 +1,11 @@ +# Boost.GIL (Generic Image Library) - tests +# +# Copyright (c) 2022 Mateusz Loskot +# +# Distributed under 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) + +import testing ; + +run is_2d_traversable.cpp ; diff --git a/test/core/virtual_2d_locator/is_2d_traversable.cpp b/test/core/virtual_2d_locator/is_2d_traversable.cpp new file mode 100644 index 0000000000..f6d3147488 --- /dev/null +++ b/test/core/virtual_2d_locator/is_2d_traversable.cpp @@ -0,0 +1,41 @@ +// +// Copyright 2022 Mateusz Loskot +// +// Distributed under 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 "test_fixture.hpp" +#include "core/pixel/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_pixel_types +{ + template + void operator()(Pixel const&) + { + using deref_t = fixture::noop_dereference_fn; + using locator_t = gil::virtual_2d_locator; + using view_t = gil::image_view; + + view_t view; + BOOST_TEST(!view.is_1d_traversable()); + } + static void run() + { + boost::mp11::mp_for_each(test_pixel_types{}); + } +}; + +int main() +{ + test_pixel_types::run(); + + return boost::report_errors(); +} diff --git a/test/core/virtual_2d_locator/test_fixture.hpp b/test/core/virtual_2d_locator/test_fixture.hpp new file mode 100644 index 0000000000..83f1b2fea2 --- /dev/null +++ b/test/core/virtual_2d_locator/test_fixture.hpp @@ -0,0 +1,76 @@ +// +// Copyright 2022 Mateusz Loskot +// +// Distributed under 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_TEST_CORE_VIRTUAL_2D_LOCATOR_TEST_FIXTURE_HPP +#define BOOST_GIL_TEST_CORE_VIRTUAL_2D_LOCATOR_TEST_FIXTURE_HPP + +#include +#include + +#include + +#include "core/pixel/test_fixture.hpp" + +namespace boost { namespace gil { namespace test { namespace fixture { + +template +struct noop_dereference_fn +{ + static constexpr bool is_mutable =false; + + using const_t = noop_dereference_fn; + using value_type = Pixel; + using reference = value_type; + using const_reference = value_type; + using argument_type = gil::point_t; + using result_type = reference; + + static_assert(std::is_same::value, "value_type != reference"); + static_assert(std::is_same::value, "value_type != result_type"); + + result_type operator()(gil::point_t const&) const + { + return result_type{}; + } +}; + +template +struct locator_dereference_fn +{ + static constexpr bool is_mutable =false; + + using const_t = locator_dereference_fn; + using value_type = Pixel; + using reference = value_type; + using const_reference = value_type; + using argument_type = gil::point_t; + using result_type = reference; + + static_assert(std::is_same::value, "value_type != reference"); + static_assert(std::is_same::value, "value_type != result_type"); + + locator_dereference_fn() = default; + locator_dereference_fn(point_t const& dims) : _dimensions(dims) {} + + result_type operator()(gil::point_t const& position) const + { + if (position.x >= _dimensions.x) + throw std::out_of_range("position x out of range"); + if (position.y >= _dimensions.y) + throw std::out_of_range("position y out of range"); + + auto pixel = pixel_generator::random(); + return pixel; + } + +private: + gil::point_t _dimensions; +}; + +}}}} // namespace boost::gil::test::fixture + +#endif From 2409be4bd6ff4c36ea990ebdaf5a7066eb65a038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Wed, 29 Jun 2022 22:00:03 +0100 Subject: [PATCH 154/193] test: Add more basic cases for image class (#423) This hopefully will begin extensive test suite for the image class to maintain decent coverage for this major class of GIL. --- test/core/image/CMakeLists.txt | 5 +- test/core/image/Jamfile | 3 +- test/core/image/alignment.cpp | 125 +++++++++++++++++++++++++++++++ test/core/image/image.cpp | 1 + test/core/image/test_fixture.hpp | 13 +++- 5 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 test/core/image/alignment.cpp diff --git a/test/core/image/CMakeLists.txt b/test/core/image/CMakeLists.txt index e121f3f9bf..2f09041198 100644 --- a/test/core/image/CMakeLists.txt +++ b/test/core/image/CMakeLists.txt @@ -7,8 +7,9 @@ # foreach(_name concepts - image - empty_dimensions) + alignment + empty_dimensions + image) set(_test t_core_image_${_name}) set(_target test_core_image_${_name}) diff --git a/test/core/image/Jamfile b/test/core/image/Jamfile index e32d74da8b..c0cf0f4ad6 100644 --- a/test/core/image/Jamfile +++ b/test/core/image/Jamfile @@ -10,5 +10,6 @@ import testing ; compile concepts.cpp ; -run image.cpp ; +run alignment.cpp ; run empty_dimensions.cpp ; +run image.cpp ; \ No newline at end of file diff --git a/test/core/image/alignment.cpp b/test/core/image/alignment.cpp new file mode 100644 index 0000000000..1ddf9b9772 --- /dev/null +++ b/test/core/image/alignment.cpp @@ -0,0 +1,125 @@ +// +// Copyright 2022 Mateusz Loskot +// +// Distributed under 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 "test_fixture.hpp" +#include "core/pixel/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_constructor_with_alignment +{ + template + void operator()(Image const&) + { + std::size_t alignment = 0; + Image image(alignment); + BOOST_TEST(image.width() == 0); + BOOST_TEST(image.height() == 0); + } + + static void run() + { + boost::mp11::mp_for_each(test_constructor_with_alignment{}); + } +}; + +struct test_constructor_with_alignment_allocator +{ + template + void operator()(Image const&) + { + std::size_t alignment = 0; + std::allocator allocator; + Image image(alignment, allocator); + BOOST_TEST(image.width() == 0); + BOOST_TEST(image.height() == 0); + } + + static void run() + { + boost::mp11::mp_for_each(test_constructor_with_alignment_allocator{}); + } +}; + +struct test_constructor_with_dimensions_alignment +{ + template + void operator()(Image const&) + { + gil::point_t const dimensions{256, 128}; + std::size_t alignment = 0; + Image image(dimensions, alignment); + BOOST_TEST(image.width() == dimensions.x); + BOOST_TEST(image.height() == dimensions.y); + } + + static void run() + { + boost::mp11::mp_for_each(test_constructor_with_dimensions_alignment{}); + } +}; + +struct test_constructor_with_dimensions_alignment_allocator +{ + template + void operator()(Image const&) + { + gil::point_t const dimensions{256, 128}; + std::size_t alignment = 0; + std::allocator allocator; + Image image(dimensions, alignment, allocator); + BOOST_TEST(image.width() == dimensions.x); + BOOST_TEST(image.height() == dimensions.y); + } + + static void run() + { + boost::mp11::mp_for_each(test_constructor_with_dimensions_alignment_allocator{}); + } +}; + +struct test_constructor_with_dimensions_pixel_alignment +{ + template + void operator()(Image const &) + { + gil::point_t const dimensions{3, 3}; + using pixel_t = typename Image::view_t::value_type; + pixel_t const rnd_pixel = fixture::pixel_generator::random(); + + Image image(dimensions, rnd_pixel, 32u); + BOOST_TEST(image.width() == dimensions.x); + BOOST_TEST(image.height() == dimensions.y); + + for (pixel_t const& p : gil::view(image)) + { + BOOST_TEST(p == rnd_pixel); + } + } + + static void run() + { + boost::mp11::mp_for_each(test_constructor_with_dimensions_pixel_alignment{}); + } +}; + +int main() +{ + test_constructor_with_alignment::run(); + test_constructor_with_alignment_allocator::run(); + test_constructor_with_dimensions_alignment::run(); + test_constructor_with_dimensions_alignment_allocator::run(); + test_constructor_with_dimensions_pixel_alignment::run(); + test_constructor_with_dimensions_pixel_alignment::run(); + + return ::boost::report_errors(); +} diff --git a/test/core/image/image.cpp b/test/core/image/image.cpp index cf73f1c7db..e974b2b79b 100644 --- a/test/core/image/image.cpp +++ b/test/core/image/image.cpp @@ -166,6 +166,7 @@ struct test_move_assignement BOOST_TEST_EQ(image.dimensions(), gil::point_t{}); } } + static void run() { boost::mp11::mp_for_each(test_move_assignement{}); diff --git a/test/core/image/test_fixture.hpp b/test/core/image/test_fixture.hpp index 0a7fc4d865..a7c95617ad 100644 --- a/test/core/image/test_fixture.hpp +++ b/test/core/image/test_fixture.hpp @@ -9,7 +9,6 @@ #define BOOST_GIL_TEST_CORE_IMAGE_TEST_FIXTURE_HPP #include -#include #include #include @@ -18,8 +17,6 @@ #include #include -#include "core/test_fixture.hpp" - namespace boost { namespace gil { namespace test { namespace fixture { using image_types = std::tuple @@ -71,6 +68,16 @@ using rgb_interleaved_image_types = std::tuple gil::rgba32_image_t >; +using bit_aligned_image_types = std::tuple +< + gil::bit_aligned_image1_type<1, gil::gray_layout_t>::type, + gil::bit_aligned_image1_type<3, gil::gray_layout_t>::type, + gil::bit_aligned_image1_type<8, gil::gray_layout_t>::type, + gil::bit_aligned_image3_type<2, 2, 2, gil::rgb_layout_t>::type, + gil::bit_aligned_image3_type<5, 6, 5, gil::rgb_layout_t>::type, + gil::bit_aligned_image3_type<6, 6, 6, gil::rgb_layout_t>::type +>; + template auto generate_image(std::ptrdiff_t size_x, std::ptrdiff_t size_y, Generator&& generate) -> Image { From 233dc53ff7888647f0ae39af70aac39e575d1e9b Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sat, 2 Jul 2022 11:00:09 +0200 Subject: [PATCH 155/193] fix: Unused variable warning (#704) --- include/boost/gil/algorithm.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/gil/algorithm.hpp b/include/boost/gil/algorithm.hpp index 5d2bb3a43d..2b61068830 100644 --- a/include/boost/gil/algorithm.hpp +++ b/include/boost/gil/algorithm.hpp @@ -155,7 +155,7 @@ auto copy( /// \ingroup STLOptimizations /// \brief Copy when both src and dst are interleaved and of the same type can be just memmove template -BOOST_FORCEINLINE +BOOST_FORCEINLINE auto copy(const boost::gil::pixel* first, const boost::gil::pixel* last, boost::gil::pixel* dst) -> boost::gil::pixel* { @@ -797,7 +797,7 @@ void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, It2 last2, planar /// std::uninitialized_copy for interleaved or mixed(planar into interleaved) iterators template BOOST_FORCEINLINE -void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, It2 last2, mixed_to_interleaved_type) +void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, It2, mixed_to_interleaved_type) { std::uninitialized_copy(first1, last1, first2); } @@ -805,7 +805,7 @@ void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, It2 last2, mixed_ /// std::uninitialized_copy for interleaved to planar iterators template BOOST_FORCEINLINE -void uninitialized_copy_aux(It1 first1, It1 last1, It2 first2, It2 last2, +void uninitialized_copy_aux(It1 first1, It1, It2 first2, It2 last2, interleaved_to_planar_type) { default_construct_aux(first2, last2, std::true_type()); From fdefeab388f43c54c911e90a01988afb459df066 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sat, 2 Jul 2022 17:06:05 +0200 Subject: [PATCH 156/193] docs: fixed gradient tutorial (#703) --- doc/images/mandel.jpg | Bin 36020 -> 15920 bytes doc/tutorial/gradient.rst | 309 +++++++++++++++++++++----------------- 2 files changed, 168 insertions(+), 141 deletions(-) diff --git a/doc/images/mandel.jpg b/doc/images/mandel.jpg index f9bb916bd2c90cbf73d5808bcdca48a34fea099c..c72001bf8968d23ad49be5a5dca9ba6d698b2ae6 100644 GIT binary patch literal 15920 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 Ut$!aoKf5?IiY|2tDZDKGA1pE@X8-^I literal 36020 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 diff --git a/doc/tutorial/gradient.rst b/doc/tutorial/gradient.rst index cc59d22e3e..8d44d3ff2f 100644 --- a/doc/tutorial/gradient.rst +++ b/doc/tutorial/gradient.rst @@ -9,11 +9,13 @@ This comprehensive (and long) tutorial will walk you through an example of using GIL to compute the image gradients. We will start with some very simple and non-generic code and make it more -generic as we go along. Let us start with a horizontal gradient and use the +generic as we go along. Let us start with a horizontal gradient and use the simplest possible approximation to a gradient - central difference. The gradient at pixel x can be approximated with the half-difference of its -two neighboring pixels:: +two neighboring pixels: + +.. code-block:: cpp D[x] = (I[x-1] - I[x+1]) / 2 @@ -33,6 +35,7 @@ Here is how the interface to our algorithm looks like: .. code-block:: cpp #include + using namespace boost::gil; void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) @@ -44,19 +47,19 @@ Here is how the interface to our algorithm looks like: ``gray8c_view_t`` is the type of the source image view - an 8-bit grayscale view, whose pixels are read-only (denoted by the "c"). -The output is a grayscale view with a 8-bit signed (denoted by the "s") +The output is a grayscale view with an 8-bit signed (denoted by the "s") integer channel type. See Appendix 1 for the complete convention GIL uses to name concrete types. GIL makes a distinction between an image and an image view. -A GIL **image view**, is a shallow, lightweight view of a rectangular grid of +A GIL *image view* is a shallow, lightweight view of a rectangular grid of pixels. It provides access to the pixels but does not own the pixels. Copy-constructing a view does not deep-copy the pixels. Image views do not propagate their constness to the pixels and should always be taken by a const reference. Whether a view is mutable or read-only (immutable) is a property of the view type. -A GIL `image`, on the other hand, is a view with associated ownership. +A GIL *image*, on the other hand, is a view with associated ownership. It is a container of pixels; its constructor/destructor allocates/deallocates the pixels, its copy-constructor performs deep-copy of the pixels and its ``operator==`` performs deep-compare of the pixels. Images also propagate @@ -78,12 +81,12 @@ your code and GIL: .. code-block:: cpp void ComputeXGradientGray8( - unsigned char const* src_pixels, ptrdiff_t src_row_bytes, + unsigned char const* src_pixels, std::ptrdiff_t src_row_bytes, int w, int h, - signed char* dst_pixels, ptrdiff_t dst_row_bytes) + signed char* dst_pixels, std::ptrdiff_t dst_row_bytes) { - gray8c_view_t src = interleaved_view(w, h, (gray8_pixel_t const*)src_pixels, src_row_bytes); - gray8s_view_t dst = interleaved_view(w, h, (gray8s_pixel_t*)dst_pixels, dst_row_bytes); + gray8c_view_t src = interleaved_view(w, h, reinterpret_cast(src_pixels), src_row_bytes); + gray8s_view_t dst = interleaved_view(w, h, reinterpret_cast(dst_pixels), dst_row_bytes); x_gradient(src, dst); } @@ -108,7 +111,7 @@ gradient like this: We use image view's ``operator(x,y)`` to get a reference to the pixel at a given location and we set it to the half-difference of its left and right -neighbors. ``operator()`` returns a reference to a grayscale pixel. +neighbors. ``operator()`` returns a reference to a grayscale pixel. A grayscale pixel is convertible to its channel type (``unsigned char`` for ``src``) and it can be copy-constructed from a channel. (This is only true for grayscale pixels). @@ -126,7 +129,7 @@ addition and multiplication. Here is a faster version of the above: gray8c_view_t::x_iterator src_it = src.row_begin(y); gray8s_view_t::x_iterator dst_it = dst.row_begin(y); - for (int x=1; x < src.width() - 1; ++x) + for (int x = 1; x < src.width() - 1; ++x) dst_it[x] = (src_it[x-1] - src_it[x+1]) / 2; } } @@ -141,7 +144,7 @@ operator. The code to compute gradient in the vertical direction is very similar: -.. code-block: cpp +.. code-block:: cpp void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) { @@ -159,8 +162,9 @@ Instead of looping over the rows, we loop over each column and create a ``y_iterator``, an iterator moving vertically. In this case a simple pointer cannot be used because the distance between two adjacent pixels equals the number of bytes in each row of the image. GIL uses here a special step -iterator class whose size is 8 bytes - it contains a raw C pointer and a step. -Its ``operator[]`` multiplies the index by its step. +iterator class whose size is twice the size of the iterator in horizontal +direction - it contains a raw C pointer and a step. Its ``operator[]`` multiplies +the index by its step. The above version of ``y_gradient``, however, is much slower (easily an order of magnitude slower) than ``x_gradient`` because of the memory access pattern; @@ -206,15 +210,15 @@ done with GIL locators: gray8c_view_t::xy_locator src_loc = src.xy_at(0,1); for (int y = 1; y < src.height() - 1; ++y) { - gray8s_view_t::x_iterator dst_it = dst.row_begin(y); + gray8s_view_t::x_iterator dst_it = dst.row_begin(y); for (int x = 0; x < src.width(); ++x) - { - (*dst_it) = (src_loc(0,-1) - src_loc(0,1)) / 2; + { + *dst_it = (src_loc(0,-1) - src_loc(0,1)) / 2; ++dst_it; ++src_loc.x(); // each dimension can be advanced separately } - src_loc+=point(-src.width(), 1); // carriage return + src_loc += point_t(-src.width(), 1); // carriage return } } @@ -229,17 +233,18 @@ simultaneously using its ``operator+=`` and ``operator-=``. Similar to image views, locators provide binary ``operator()`` which returns a reference to a pixel with a relative offset to the current locator position. For example, ``src_loc(0,1)`` returns a reference to the -neighbor below the current pixel. Locators are very lightweight -objects - in the above example the locator has a size of 8 bytes - it -consists of a raw pointer to the current pixel and an int indicating +neighbor below the current pixel. Locators are very lightweight +objects - they consist of a raw pointer to the current pixel and an int indicating the number of bytes from one row to the next (which is the step when moving vertically). The call to ``++src_loc.x()`` corresponds to a -single C pointer increment. However, the example above performs more +single C pointer increment. However, the example above performs more computations than necessary. The code ``src_loc(0,1)`` has to compute -the offset of the pixel in two dimensions, which is slow. Notice +the offset of the pixel in two dimensions, which is slow. Notice though that the offset of the two neighbors is the same, regardless of the pixel location. To improve the performance, GIL can cache and -reuse this offset:: +reuse this offset: + +.. code-block:: cpp void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) { @@ -252,12 +257,12 @@ reuse this offset:: gray8s_view_t::x_iterator dst_it = dst.row_begin(y); for (int x = 0; x < src.width(); ++x) - { - (*dst_it) = (src_loc[above] - src_loc[below]) / 2; + { + *dst_it = (src_loc[above] - src_loc[below]) / 2; ++dst_it; ++src_loc.x(); } - src_loc+=point(-src.width(), 1); + src_loc += point_t(-src.width(), 1); } } @@ -276,10 +281,10 @@ Here is how the new interface looks like: .. code-block:: cpp template - void x_gradient(const SrcView& src, const DstView& dst) + void x_gradient(SrcView const& src, DstView const& dst) { - gil_function_requires >(); - gil_function_requires >(); + gil_function_requires>(); + gil_function_requires>(); gil_function_requires < ColorSpacesCompatibleConcept @@ -293,8 +298,8 @@ Here is how the new interface looks like: } The new algorithm now takes the types of the input and output image -views as template parameters. That allows using both built-in GIL -image views, as well as any user-defined image view classes. The +views as template parameters. That allows using both built-in GIL +image views, as well as any user-defined image view classes. The first three lines are optional; they use ``boost::concept_check`` to ensure that the two arguments are valid GIL image views, that the second one is mutable and that their color spaces are compatible @@ -302,20 +307,20 @@ second one is mutable and that their color spaces are compatible GIL does not require using its own built-in constructs. You are free to use your own channels, color spaces, iterators, locators, views and -images. However, to work with the rest of GIL they have to satisfy a +images. However, to work with the rest of GIL they have to satisfy a set of requirements; in other words, they have to \e model the -corresponding GIL _concept_. GIL's concepts are defined in the user +corresponding GIL *concept*. GIL's concepts are defined in the user guide. One of the biggest drawbacks of using templates and generic programming in C++ is that compile errors can be very difficult to -comprehend. This is a side-effect of the lack of early type +comprehend. This is a side-effect of the lack of early type checking - a generic argument may not satisfy the requirements of a function, but the incompatibility may be triggered deep into a nested -call, in code unfamiliar and hardly related to the problem. GIL uses +call, in code unfamiliar and hardly related to the problem. GIL uses ``boost::concept_check`` to mitigate this problem. The above three lines of code check whether the template parameters are valid models -of their corresponding concepts. If a model is incorrect, the compile +of their corresponding concepts. If a model is incorrect, the compile error will be inside ``gil_function_requires``, which is much closer to the problem and easier to track. Furthermore, such checks get compiled out and have zero performance overhead. The disadvantage of @@ -331,15 +336,15 @@ channels of the pixel and compute the gradient for each channel: .. code-block:: cpp template - void x_gradient(const SrcView& src, const DstView& dst) + void x_gradient(SrcView const& src, DstView const& dst) { - for (int y=0; y < src.height(); ++y) + for (int y = 0; y < src.height(); ++y) { typename SrcView::x_iterator src_it = src.row_begin(y); typename DstView::x_iterator dst_it = dst.row_begin(y); for (int x = 1; x < src.width() - 1; ++x) - for (int c = 0; c < num_channels::value; ++c) + for (std::size_t c = 0; c < num_channels::value; ++c) dst_it[x][c] = (src_it[x-1][c]- src_it[x+1][c]) / 2; } } @@ -352,23 +357,24 @@ GIL allows us to abstract out such per-channel operations: template struct halfdiff_cast_channels { - template Out operator()(T const& in1, T const& in2) const + template + auto operator()(T const& in1, T const& in2) const -> Out { return Out((in1 - in2) / 2); } }; template - void x_gradient(const SrcView& src, const DstView& dst) + void x_gradient(SrcView const& src, DstView const& dst) { - typedef typename channel_type::type dst_channel_t; + using dst_channel_t = typename channel_type::type; - for (int y=0; y < src.height(); ++y) + for (int y = 0; y < src.height(); ++y) { typename SrcView::x_iterator src_it = src.row_begin(y); typename DstView::x_iterator dst_it = dst.row_begin(y); - for (int x=1; x < src.width() - 1; ++x) + for (int x = 1; x < src.width() - 1; ++x) { static_transform(src_it[x-1], src_it[x+1], dst_it[x], halfdiff_cast_channels()); @@ -383,11 +389,11 @@ Other such algorithms are ``static_generate``, ``static_fill`` and GIL channel algorithms use static recursion to unroll the loops; they never loop over the channels explicitly. -Note that sometimes modern compilers (at least Visual Studio 8) already unroll -channel-level loops, such as the one above. However, another advantage of -using GIL's channel-level algorithms is that they pair the channels -semantically, not based on their order in memory. For example, the above -example will properly match an RGB source with a BGR destination. +Note that sometimes modern compilers already unroll channel-level loops +such as the one above. However, another advantage of using GIL's channel-level +algorithms is that they pair the channels semantically, not based on their +order in memory. For example, the above example will properly match an +RGB source with a BGR destination. Here is how we can use our generic version with images of different types: @@ -395,34 +401,34 @@ Here is how we can use our generic version with images of different types: // Calling with 16-bit grayscale data void XGradientGray16_Gray32( - unsigned short const* src_pixels, ptrdiff_t src_row_bytes, + unsigned short const* src_pixels, std::ptrdiff_t src_row_bytes, int w, int h, - signed int* dst_pixels, ptrdiff_t dst_row_bytes) + signed int* dst_pixels, std::ptrdiff_t dst_row_bytes) { - gray16c_view_t src=interleaved_view(w, h, (gray16_pixel_t const*)src_pixels, src_row_bytes); - gray32s_view_t dst=interleaved_view(w, h, (gray32s_pixel_t*)dst_pixels, dst_row_bytes); + gray16c_view_t src = interleaved_view(w, h, reinterpret_cast(src_pixels), src_row_bytes); + gray32s_view_t dst = interleaved_view(w, h, reinterpret_cast(dst_pixels), dst_row_bytes); x_gradient(src,dst); } // Calling with 8-bit RGB data into 16-bit BGR void XGradientRGB8_BGR16( - unsigned char const* src_pixels, ptrdiff_t src_row_bytes, + unsigned char const* src_pixels, std::ptrdiff_t src_row_bytes, int w, int h, - signed short* dst_pixels, ptrdiff_t dst_row_bytes) + signed short* dst_pixels, std::ptrdiff_t dst_row_bytes) { - rgb8c_view_t src = interleaved_view(w, h, (rgb8_pixel_t const*)src_pixels, src_row_bytes); - bgr16s_view_t dst = interleaved_view(w, h, (bgr16s_pixel_t*)dst_pixels, dst_row_bytes); + rgb8c_view_t src = interleaved_view(w, h, reinterpret_cast(src_pixels), src_row_bytes); + bgr16s_view_t dst = interleaved_view(w, h, reinterpret_cast(dst_pixels), dst_row_bytes); x_gradient(src, dst); } // Either or both the source and the destination could be planar - the gradient code does not change void XGradientPlanarRGB8_RGB32( unsigned short const* src_r, unsigned short const* src_g, unsigned short const* src_b, - ptrdiff_t src_row_bytes, int w, int h, - signed int* dst_pixels, ptrdiff_t dst_row_bytes) + std::ptrdiff_t src_row_bytes, int w, int h, + signed int* dst_pixels, std::ptrdiff_t dst_row_bytes) { rgb16c_planar_view_t src = planar_rgb_view (w, h, src_r, src_g, src_b, src_row_bytes); - rgb32s_view_t dst = interleaved_view(w, h,(rgb32s_pixel_t*)dst_pixels, dst_row_bytes); + rgb32s_view_t dst = interleaved_view(w, h, reinterpret_cast(dst_pixels), dst_row_bytes); x_gradient(src,dst); } @@ -445,7 +451,7 @@ Here is how to do this in GIL: .. code-block:: cpp template - void y_gradient(const SrcView& src, const DstView& dst) + void y_gradient(SrcView const& src, DstView const& dst) { x_gradient(rotated90ccw_view(src), rotated90ccw_view(dst)); } @@ -470,16 +476,16 @@ channel of a color image. Here is how to do that: .. code-block:: cpp template - void nth_channel_x_gradient(const SrcView& src, int n, const DstView& dst) + void nth_channel_x_gradient(SrcView const& src, int n, DstView const& dst) { x_gradient(nth_channel_view(src, n), dst); } ``nth_channel_view`` is a view transformation function that takes any view and returns a single-channel (grayscale) view of its N-th -channel. For interleaved RGB view, for example, the returned view is +channel. For interleaved RGB view, for example, the returned view is a step view - a view whose horizontal iterator skips over two channels -when incremented. If applied on a planar RGB view, the returned type +when incremented. If applied on a planar RGB view, the returned type is a simple grayscale view whose horizontal iterator is a C pointer. Image view transformation functions can be piped together. For example, to compute the y gradient of the second channel of the even @@ -487,7 +493,7 @@ pixels in the view, use: .. code-block:: cpp - y_gradient(subsampled_view(nth_channel_view(src, 1), 2,2), dst); + y_gradient(subsampled_view(nth_channel_view(src, 1), 2, 2), dst); GIL can sometimes simplify piped views. For example, two nested subsampled views (views that skip over pixels in X and in Y) can be @@ -497,7 +503,7 @@ the steps of the two views. 1D pixel iterators ------------------ -Let's go back to ``x_gradient`` one more time. Many image view +Let's go back to ``x_gradient`` one more time. Many image view algorithms apply the same operation for each pixel and GIL provides an abstraction to handle them. However, our algorithm has an unusual access pattern, as it skips the first and the last column. It would be @@ -510,7 +516,7 @@ and last column: void x_gradient_unguarded(gray8c_view_t const& src, gray8s_view_t const& dst) { - for (int y=0; y < src.height(); ++y) + for (int y = 0; y < src.height(); ++y) { gray8c_view_t::x_iterator src_it = src.row_begin(y); gray8s_view_t::x_iterator dst_it = dst.row_begin(y); @@ -522,7 +528,7 @@ and last column: void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) { - assert(src.width()>=2); + assert(src.width() >= 2); x_gradient_unguarded(subimage_view(src, 1, 0, src.width()-2, src.height()), subimage_view(dst, 1, 0, src.width()-2, src.height())); } @@ -542,7 +548,7 @@ rewrite it more compactly: void x_gradient_unguarded(gray8c_view_t const& src, gray8s_view_t const& dst) { gray8c_view_t::iterator src_it = src.begin(); - for (gray8s_view_t::iterator dst_it = dst.begin(); dst_it!=dst.end(); ++dst_it, ++src_it) + for (gray8s_view_t::iterator dst_it = dst.begin(); dst_it != dst.end(); ++dst_it, ++src_it) *dst_it = (src_it.x()[-1] - src_it.x()[1]) / 2; } @@ -569,7 +575,7 @@ GIL provides STL equivalents of many algorithms. For example, destination range the result of a generic function taking the corresponding element of the source range. In our example, we want to assign to each destination pixel the value of the half-difference of -the horizontal neighbors of the corresponding source pixel. If we +the horizontal neighbors of the corresponding source pixel. If we abstract that operation in a function object, we can use GIL's ``transform_pixel_positions`` to do that: @@ -577,7 +583,7 @@ abstract that operation in a function object, we can use GIL's struct half_x_difference { - int operator()(const gray8c_loc_t& src_loc) const + auto operator()(gray8c_loc_t const& src_loc) const -> int { return (src_loc.x()[-1] - src_loc.x()[1]) / 2; } @@ -594,7 +600,7 @@ GIL provides the algorithms ``for_each_pixel`` and ``for_each_pixel_position`` and ``transform_pixel_positions``, which instead of references to pixels, pass to the generic function pixel locators. This allows for more powerful functions that can use the -pixel neighbors through the passed locators. GIL algorithms iterate +pixel neighbors through the passed locators. GIL algorithms iterate through the pixels using the more efficient two nested loops (as opposed to the single loop using 1-D iterators) @@ -647,7 +653,7 @@ Image The above example has a performance problem - ``x_gradient`` dereferences most source pixels twice, which will cause the above code -to perform color conversion twice. Sometimes it may be more efficient +to perform color conversion twice. Sometimes it may be more efficient to copy the color converted image into a temporary buffer and use it to compute the gradient - that way color conversion is invoked once per pixel. Using our non-generic version we can do it like this: @@ -664,7 +670,7 @@ per pixel. Using our non-generic version we can do it like this: First we construct an 8-bit grayscale image with the same dimensions as our source. Then we copy a color-converted view of the source into -the temporary image. Finally we use a read-only view of the temporary +the temporary image. Finally we use a read-only view of the temporary image in our ``x_gradient algorithm``. As the example shows, GIL provides global functions ``view`` and ``const_view`` that take an image and return a mutable or an immutable view of its pixels. @@ -674,10 +680,10 @@ Creating a generic version of the above is a bit trickier: .. code-block:: cpp template - void x_luminosity_gradient(const SrcView& src, const DstView& dst) + void x_luminosity_gradient(SrcView const& src, DstView const& dst) { using d_channel_t = typename channel_type::type; - using channel_t = typename channel_convert_to_unsigned::type; + using channel_t = typename detail::channel_convert_to_unsigned::type; using gray_pixel_t = pixel; using gray_image_t = image; @@ -706,7 +712,7 @@ metafunction to remove its sign (if it is a signed integral type) and then use it to generate the type of a grayscale pixel. From the pixel type we create the image type. GIL's image class is specialized over the pixel type and a boolean indicating whether the image should be -planar or interleaved. Single-channel (grayscale) images in GIL must +planar or interleaved. Single-channel (grayscale) images in GIL must always be interleaved. There are multiple ways of constructing types in GIL. Instead of instantiating the classes directly we could have used type factory metafunctions. The following code is equivalent: @@ -716,10 +722,9 @@ used type factory metafunctions. The following code is equivalent: template void x_luminosity_gradient(SrcView const& src, DstView const& dst) { - typedef typename channel_type::type d_channel_t; - typedef typename channel_convert_to_unsigned::type channel_t; - typedef typename image_type::type gray_image_t; - typedef typename gray_image_t::value_type gray_pixel_t; + using d_channel_t = typename channel_type::type; + using channel_t = typename detail::channel_convert_to_unsigned::type; + using gray_image_t = typename image_type::type; gray_image_t ccv_image(src.dimensions()); copy_and_convert_pixels(src, view(ccv_image)); @@ -751,77 +756,94 @@ Virtual Image Views So far we have been dealing with images that have pixels stored in memory. GIL allows you to create an image view of an arbitrary image, including a synthetic function. To demonstrate this, let us create a -view of the Mandelbrot set. First, we need to create a function +view of the Mandelbrot set. First, we need to create a function object that computes the value of the Mandelbrot set at a given location (x,y) in the image: .. code-block:: cpp // models PixelDereferenceAdaptorConcept + template // Models PixelValueConcept struct mandelbrot_fn { - typedef point point_t; - - typedef mandelbrot_fn const_t; - typedef gray8_pixel_t value_type; - typedef value_type reference; - typedef value_type const_reference; - typedef point_t argument_type; - typedef reference result_type; + using point_t = point; + using const_t = mandelbrot_fn; + using value_type = Pixel; + using reference = value_type; + using const_reference = value_type; + using argument_type = point_t; + using result_type = reference; static bool constexpr is_mutable = false; - mandelbrot_fn() {} - mandelbrot_fn(const point_t& sz) : _img_size(sz) {} + mandelbrot_fn() = default; + mandelbrot_fn(point_t const& sz, value_type const& in_color, value_type const& out_color) + : _in_color(in_color), _out_color(out_color), _img_size(sz) {} - result_type operator()(const point_t& p) const + auto operator()(point_t const& p) const -> result_type { // normalize the coords to (-2..1, -1.5..1.5) - double t=get_num_iter(point(p.x/(double)_img_size.x*3-2, p.y/(double)_img_size.y*3-1.5f)); - return value_type((bits8)(pow(t,0.2)*255)); // raise to power suitable for viewing + // (actually make y -1.0..2 so it is asymmetric, so we can verify some view factory methods) + double t = get_num_iter(point(p.x/(double)_img_size.x*3-2, p.y/(double)_img_size.y*3-1.0f));//1.5f)); + t = pow(t,0.2); + + result_type ret; + for (std::size_t k = 0; k::value; ++k) + ret[k] = typename channel_type::type(_in_color[k]*t + _out_color[k]*(1-t)); + return ret; } + private: + value_type _in_color,_out_color; point_t _img_size; + static constexpr int MAX_ITER = 100; - double get_num_iter(const point& p) const + auto get_num_iter(point const& p) const -> double { point Z(0,0); - for (int i=0; i<100; ++i) // 100 iterations - { + for (int i = 0; i < MAX_ITER; ++i) + { Z = point(Z.x*Z.x - Z.y*Z.y + p.x, 2*Z.x*Z.y + p.y); if (Z.x*Z.x + Z.y*Z.y > 4) - return i/(double)100; + return i/static_cast(MAX_ITER); } return 0; } }; We can now use GIL's ``virtual_2d_locator`` with this function object -to construct a Mandelbrot view of size 200x200 pixels: +to construct a rgb8 Mandelbrot view of size 200x200 pixels: .. code-block:: cpp - typedef mandelbrot_fn::point_t point_t; - typedef virtual_2d_locator locator_t; - typedef image_view my_virt_view_t; + using deref_t = mandelbrot_fn; + using point_t = deref_t::point_t; + using locator_t = virtual_2d_locator; + using my_virt_view_t = image_view; point_t dims(200,200); // Construct a Mandelbrot view with a locator, taking top-left corner (0,0) and step (1,1) - my_virt_view_t mandel(dims, locator_t(point_t(0,0), point_t(1,1), mandelbrot_fn(dims))); + my_virt_view_t mandel(dims, locator_t( + point_t(0,0), point_t(1,1), + deref_t(dims, rgb8_pixel_t(255, 0, 255), rgb8_pixel_t(0, 255, 0)))); + We can treat the synthetic view just like a real one. For example, -let's invoke our ``x_gradient`` algorithm to compute the gradient of +let's invoke our ``x_luminosity_gradient`` algorithm to compute the luminosity gradient of the 90-degree rotated view of the Mandelbrot set and save the original and the result: .. code-block:: cpp + #include + gray8s_image_t img(dims); - x_gradient(rotated90cw_view(mandel), view(img)); + x_luminosity_gradient(rotated90cw_view(mandel), view(img)); - // Save the Mandelbrot set and its 90-degree rotated gradient (jpeg cannot save signed char; must convert to unsigned char) - jpeg_write_view("mandel.jpg",mandel); - jpeg_write_view("mandel_grad.jpg",color_converted_view(const_view(img))); + // Save the Mandelbrot set and its 90-degree rotated gradient + // (jpeg cannot save signed char; must convert to unsigned char) + write_view("mandel.jpg", mandel, jpeg_tag{}); + write_view("mandel_grad.jpg", color_converted_view(const_view(img)), jpeg_tag{}); Here is what the two files look like: @@ -831,23 +853,23 @@ Run-Time Specified Images and Image Views ----------------------------------------- So far we have created a generic function that computes the image -gradient of an image view template specialization. Sometimes, +gradient of an image view template specialization. Sometimes, however, the properties of an image view, such as its color space and -channel depth, may not be available at compile time. GIL's +channel depth, may not be available at compile time. GIL's ``dynamic_image`` extension allows for working with GIL constructs -that are specified at run time, also called _variants_. GIL provides +that are specified at run time, also called *variants*. GIL provides models of a run-time instantiated image, ``any_image``, and a run-time instantiated image view, ``any_image_view``. The mechanisms are in place to create other variants, such as ``any_pixel``, -``any_pixel_iterator``, etc. Most of GIL's algorithms and all of the +``any_pixel_iterator``, etc. Most of GIL's algorithms and all of the view transformation functions also work with run-time instantiated image views and binary algorithms, such as ``copy_pixels`` can have either or both arguments be variants. Lets make our ``x_luminosity_gradient`` algorithm take a variant image view. For simplicity, let's assume that only the source view can be a -variant. (As an example of using multiple variants, see GIL's image -view algorithm overloads taking multiple variants.) +variant. As an example of using multiple variants, see GIL's image +view algorithm overloads taking multiple variants. First, we need to make a function object that contains the templated destination view and has an application operator taking a templated @@ -860,23 +882,26 @@ source view: template struct x_gradient_obj { - typedef void result_type; // required typedef + using result_type = void; // required typedef - const DstView& _dst; - x_gradient_obj(const DstView& dst) : _dst(dst) {} + DstView const& _dst; + x_gradient_obj(DstView const& dst) : _dst(dst) {} template - void operator()(const SrcView& src) const { x_luminosity_gradient(src, _dst); } + auto operator()(SrcView const& src) const -> result_type + { + x_luminosity_gradient(src, _dst); + } }; The second step is to provide an overload of ``x_luminosity_gradient`` that -takes image view variant and calls ``variant2::visit`` passing it the +takes an image view variant and calls ``variant2::visit`` passing it the function object: .. code-block:: cpp template - void x_luminosity_gradient(const any_image_view& src, const DstView& dst) + void x_luminosity_gradient(any_image_view const& src, DstView const& dst) { variant2::visit(x_gradient_obj(dst), src); } @@ -885,7 +910,7 @@ function object: templated over ``SrcViews...``, an enumeration of all possible view types the variant can take. ``src`` contains inside an index of the currently instantiated type, as well as a block of memory containing -the instance. ``variant2::visit`` goes through a switch statement +the instance. ``variant2::visit`` goes through a switch statement over the index, each case of which casts the memory to the correct view type and invokes the function object with it. Invoking an algorithm on a variant has the overhead of one switch @@ -897,39 +922,41 @@ Here is how we can construct a variant and invoke the algorithm: .. code-block:: cpp - #include - #include + #include - typedef mp11::mp_list my_img_types; - any_image runtime_image; - jpeg_read_image("input.jpg", runtime_image); + using my_img = any_image; + my_img runtime_image; + read_image("input.jpg", runtime_image, jpeg_tag{}); gray8s_image_t gradient(runtime_image.dimensions()); x_luminosity_gradient(const_view(runtime_image), view(gradient)); - jpeg_write_view("x_gradient.jpg", color_converted_view(const_view(gradient))); + write_view( + "x_gradient.jpg", + color_converted_view(const_view(gradient)), + jpeg_tag{}); In this example, we create an image variant that could be 8-bit or 16-bit RGB or grayscale image. We then use GIL's I/O extension to load the image from file in its native color space and channel depth. If none of the allowed image types matches the image on disk, an -exception will be thrown. We then construct a 8 bit signed -(i.e. ``char``) image to store the gradient and invoke ``x_gradient`` -on it. Finally we save the result into another file. We save the view +exception will be thrown. We then construct an 8 bit signed +(i.e. ``char``) image to store the gradient and invoke ``x_luminosity_gradient`` +on it. Finally we save the result into another file. We save the view converted to 8-bit unsigned, because JPEG I/O does not support signed char. -Note how free functions and methods such as ``jpeg_read_image``, +Note how free functions and methods such as ``read_image``, ``dimensions``, ``view`` and ``const_view`` work on both templated and -variant types. For templated images ``view(img)`` returns a templated -view, whereas for image variants it returns a view variant. For +variant types. For templated images ``view(img)`` returns a templated +view, whereas for image variants it returns a view variant. For example, the return type of ``view(runtime_image)`` is ``any_image_view`` where ``Views`` enumerates four views -corresponding to the four image types. ``const_view(runtime_image)`` +corresponding to the four image types. ``const_view(runtime_image)`` returns a ``any_image_view`` of the four read-only view types, etc. A warning about using variants: instantiating an algorithm with a variant effectively instantiates it with every possible type the -variant can take. For binary algorithms, the algorithm is +variant can take. For binary algorithms, the algorithm is instantiated with every possible combination of the two input types! This can take a toll on both the compile time and the executable size. @@ -937,7 +964,7 @@ Conclusion ---------- This tutorial provides a glimpse at the challenges associated with -writing generic and efficient image processing algorithms in GIL. We +writing generic and efficient image processing algorithms in GIL. We have taken a simple algorithm and shown how to make it work with image representations that vary in bit depth, color space, ordering of the channels, and planar/interleaved structure. We have demonstrated that @@ -953,8 +980,8 @@ work on homogeneous images, i.e. images whose pixels have channels that are all of the same type. There are examples of images, such as a packed 565 RGB format, which contain channels of different types. While GIL provides concepts and algorithms operating on -heterogeneous pixels, we leave the task of extending x_gradient as an -exercise for the reader. Second, after computing the value of the +heterogeneous pixels, we leave the task of extending ``x_gradient`` as an +exercise for the reader. Second, after computing the value of the gradient we are simply casting it to the destination channel type. This may not always be the desired operation. For example, if the source channel is a float with range [0..1] and the destination is @@ -962,7 +989,7 @@ unsigned char, casting the half-difference to unsigned char will result in either 0 or 1. Instead, what we might want to do is scale the result into the range of the destination channel. GIL's channel-level algorithms might be useful in such cases. For example, -\p channel_convert converts between channels by linearly scaling the +``channel_convert`` converts between channels by linearly scaling the source channel value into the range of the destination channel. There is a lot to be done in improving the performance as From 573ba132cd182f0bde819d1f1abcd13adcf00623 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Mon, 4 Jul 2022 21:09:21 +0200 Subject: [PATCH 157/193] fix: broken build for midpoint_ellipse_rasterizer::draw_curve (#705) * fix: ellipse_rasterizer draw_curve() * moved ostream operator overload of point into test fixture file --- .../gil/extension/rasterization/ellipse.hpp | 2 +- test/core/point/test_fixture.hpp | 22 ++++++++++++ test/extension/rasterization/Jamfile | 1 + .../rasterization/apply_rasterizer.cpp | 35 +++++++++++++++++++ test/extension/rasterization/circle.cpp | 15 ++++---- test/extension/rasterization/ellipse.cpp | 6 +++- test/extension/rasterization/line.cpp | 25 +++++-------- 7 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 test/core/point/test_fixture.hpp create mode 100644 test/extension/rasterization/apply_rasterizer.cpp diff --git a/include/boost/gil/extension/rasterization/ellipse.hpp b/include/boost/gil/extension/rasterization/ellipse.hpp index 091189c788..27ca5de747 100644 --- a/include/boost/gil/extension/rasterization/ellipse.hpp +++ b/include/boost/gil/extension/rasterization/ellipse.hpp @@ -183,7 +183,7 @@ struct midpoint_ellipse_rasterizer template void operator()(View& view, Pixel const& pixel) const { - draw_curve(view, obtain_trajectory()); + draw_curve(view, pixel, obtain_trajectory()); } point center; diff --git a/test/core/point/test_fixture.hpp b/test/core/point/test_fixture.hpp new file mode 100644 index 0000000000..97b910e3a2 --- /dev/null +++ b/test/core/point/test_fixture.hpp @@ -0,0 +1,22 @@ +// +// Copyright 2022 Marco Langer +// +// 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 + +namespace boost { namespace gil { + +// necessary to use point directly with boost test macros +template +auto operator<<(std::ostream& os, point const& p) -> std::ostream& +{ + return os << "{x=" << p.x << ", y=" << p.y << "}"; +} + +}} // namespace boost::gil diff --git a/test/extension/rasterization/Jamfile b/test/extension/rasterization/Jamfile index 6be4cd9705..71f8855d01 100644 --- a/test/extension/rasterization/Jamfile +++ b/test/extension/rasterization/Jamfile @@ -8,6 +8,7 @@ # import testing ; +run apply_rasterizer.cpp ; run line.cpp ; run circle.cpp ; run ellipse.cpp ; diff --git a/test/extension/rasterization/apply_rasterizer.cpp b/test/extension/rasterization/apply_rasterizer.cpp new file mode 100644 index 0000000000..589ec1c718 --- /dev/null +++ b/test/extension/rasterization/apply_rasterizer.cpp @@ -0,0 +1,35 @@ +// +// Copyright 2022 Marco Langer +// +// 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; + +template +void test_apply_rasterizers(Rasterizer const& rasterizer) +{ + gil::rgb8_image_t image(200, 200); + gil::rgb8_pixel_t pixel{255, 0, 0}; + + apply_rasterizer(view(image), rasterizer, pixel); +} + +int main() +{ + test_apply_rasterizers(gil::midpoint_circle_rasterizer{{50, 50}, 30}); + test_apply_rasterizers(gil::trigonometric_circle_rasterizer{{50, 50}, 30}); + test_apply_rasterizers(gil::midpoint_ellipse_rasterizer{{50, 50}, {30, 20}}); + test_apply_rasterizers(gil::bresenham_line_rasterizer{{50, 50}, {30, 20}}); + + return boost::report_errors(); +} diff --git a/test/extension/rasterization/circle.cpp b/test/extension/rasterization/circle.cpp index 4df90adf4c..99b3133656 100644 --- a/test/extension/rasterization/circle.cpp +++ b/test/extension/rasterization/circle.cpp @@ -1,4 +1,3 @@ -// Boost.GIL (Generic Image Library) - tests // // Copyright 2020 Olzhas Zhumabek // @@ -7,9 +6,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include +#include + #include -#include "boost/gil/extension/rasterization/circle.hpp" -#include + +#include +#include #include namespace gil = boost::gil; @@ -17,7 +20,7 @@ namespace gil = boost::gil; template void test_rasterizer_follows_equation(Rasterizer rasterizer) { - const std::ptrdiff_t radius = rasterizer.radius; + std::ptrdiff_t const radius = rasterizer.radius; std::vector circle_points(rasterizer.point_count()); std::ptrdiff_t const r_squared = radius * radius; rasterizer(circle_points.begin()); @@ -28,9 +31,9 @@ void test_rasterizer_follows_equation(Rasterizer rasterizer) first_octant[octant_index] = circle_points[i]; } - for (const auto& point : first_octant) + for (auto const& point : first_octant) { - double y_exact = std::sqrt(r_squared - point.x * point.x); + double const y_exact = std::sqrt(r_squared - point.x * point.x); std::ptrdiff_t lower_result = static_cast(std::floor(y_exact)); std::ptrdiff_t upper_result = static_cast(std::ceil(y_exact)); BOOST_TEST(point.y >= lower_result && point.y <= upper_result); diff --git a/test/extension/rasterization/ellipse.cpp b/test/extension/rasterization/ellipse.cpp index 974ca7f87e..837301733f 100644 --- a/test/extension/rasterization/ellipse.cpp +++ b/test/extension/rasterization/ellipse.cpp @@ -5,10 +5,14 @@ // 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 +#include #include namespace gil = boost::gil; diff --git a/test/extension/rasterization/line.cpp b/test/extension/rasterization/line.cpp index f68f904f9a..dd48393ecc 100644 --- a/test/extension/rasterization/line.cpp +++ b/test/extension/rasterization/line.cpp @@ -1,4 +1,3 @@ -// Boost.GIL (Generic Image Library) - tests // // Copyright 2020 Olzhas Zhumabek // @@ -7,30 +6,22 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#include +#include "core/point/test_fixture.hpp" + +#include +#include + #include -#include "boost/gil/point.hpp" -#include "boost/gil/extension/rasterization/line.hpp" +#include #include -#include +#include #include #include #include namespace gil = boost::gil; -namespace boost -{ -namespace gil -{ -std::ostream& operator<<(std::ostream& os, const point_t p) -{ - os << "{x=" << p.x << ", y=" << p.y << "}"; - return os; -} -}} // namespace boost::gil - using line_type = std::vector; struct endpoints @@ -55,7 +46,7 @@ line_type create_line(endpoints points) return forward_line; } -void test_start_end(const line_type& line_points, endpoints points) +void test_start_end(line_type const& line_points, endpoints points) { BOOST_TEST_EQ(line_points.front(), points.start); BOOST_TEST_EQ(line_points.back(), points.end); From eb161402e7608a9d6a6266f9ba08141cc42d5570 Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Tue, 5 Jul 2022 10:28:50 +0200 Subject: [PATCH 158/193] docs: Updated readme language badge to C++14 (#707) [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 581e83dea5..d71fe15a1f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Boost Generic Image Library (GIL)](https://raw.githubusercontent.com/boostorg/gil/develop/doc/_static/gil.png) -[![Language](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) +[![Language](https://img.shields.io/badge/C%2B%2B-14-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Documentation](https://img.shields.io/badge/gil-documentation-blue.svg)](http://boostorg.github.com/gil/) [![Wiki](https://img.shields.io/badge/gil-wiki-blue.svg)](https://github.com/boostorg/gil/wiki) From 2d50022f56bd3a470ac64b0a52713737d3074067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 5 Jul 2022 23:17:38 +0200 Subject: [PATCH 159/193] docs: Update release notes for range of 36a45e3af..eb161402e [ci skip] Part of task #667 --- RELEASES.md | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index ca2574ae64..f658df133b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -8,20 +8,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). NOTICE: We are planning BREAKING switch to C++17 as minimum required C++ language version in one or two releases after Boost 1.80 ([Discussion #676](https://github.com/boostorg/gil/discussions/676)) ### Added +- GSoC 2020: Added Perona-Malik anisotropic diffusion algorithm [[PR #500](https://github.com/boostorg/gil/pull/500)) +- GSoC 2020: Added histogram class and related functionality ([PR #499](https://github.com/boostorg/gil/pull/499)) +- GSoC 2020: Added histogram equalization feature ([PR #514](https://github.com/boostorg/gil/pull/514)) +- GSoC 2020: Added histogram matching algorithm ([PR #515](https://github.com/boostorg/gil/pull/515)) +- GSoC 2020: Added ability to stack images either horizontally (`hstack`) or vertically (`vstack`) ([PR #506](https://github.com/boostorg/gil/pull/506)) +- GSoC 2020: Added adaptive histogram equalization algorithm ([PR #516](https://github.com/boostorg/gil/pull/516)) +- GSoC 2020: Added Standard Hough Transform and circle rasterization ([PR #512](https://github.com/boostorg/gil/pull/512)) +- GSoC 2020: Added Bresenham's algorithm for line rasterization ([PR #512](https://github.com/boostorg/gil/pull/512)) +- GSoC 2021: Added rotation of image by arbitrary angle around its center ([PR #565](https://github.com/boostorg/gil/pull/565)) +- GSoC 2021: Added rasterization support for ellipse based on "An Efficient Ellipse-Drawing Algorithm" by Jerry Van Aken ([PR #585](https://github.com/boostorg/gil/pull/585)) - Added `image` constructor from compatible view ([PR #520](https://github.com/boostorg/gil/pull/520)) - Added inverse function for affine `matrix3x2` ([PR #527](https://github.com/boostorg/gil/pull/527)) -- Added Perona-Malik anisotropic diffusion algorithm [[PR #500](https://github.com/boostorg/gil/pull/500)) -- GSoC 2020: Add histogram class and related functionality ([PR #499](https://github.com/boostorg/gil/pull/499)) -- GSoC 2020: Add histogram equalization feature ([PR #514](https://github.com/boostorg/gil/pull/514)) -- GSoC 2020: Add histogram matching algorithm ([PR #515](https://github.com/boostorg/gil/pull/515)) -- Added ability to stack images either horizontally (`hstack`) or vertically (`vstack`) ([PR #506](https://github.com/boostorg/gil/pull/506)) -- Added adaptive histogram equalization algorithm ([PR #516](https://github.com/boostorg/gil/pull/516)) -- Added Standard Hough Transform and circle rasterization ([PR #512](https://github.com/boostorg/gil/pull/512)) -- Added Bresenham's algorithm for line rasterization ([PR #512](https://github.com/boostorg/gil/pull/512)) - Added standard morphological transformations ([PR #541](https://github.com/boostorg/gil/pull/541)) -- Added rotation of image by arbitrary angle around its center ([PR #565](https://github.com/boostorg/gil/pull/565)) -- Added rasterization support for ellipse based on "An Efficient Ellipse-Drawing Algorithm" by Jerry Van Aken ([PR #585](https://github.com/boostorg/gil/pull/585)) - Added `for_each_pixel` overload for `any_image` ([PR #648](https://github.com/boostorg/gil/pull/648)) +- Added C++17 polymorphic memory resource typedefs for `image` class ([PR #529](https://github.com/boostorg/gil/pull/529)) ### Changed - BREAKING: The required minimum C++ version is changed from from C++11 to C++14. @@ -31,20 +32,25 @@ NOTICE: We are planning BREAKING switch to C++17 as minimum required C++ languag - BREAKING: `any_color_converted_view()` is deprecated and will be removed in the next release. Use `color_converted_view()` instead, which provides the same feature. - BREAKING: `apply_operation` for `any_image` is deprecated and will be removed in the next release. - Use `variant2::visit` instead, which provides the same feature. + Use `variant2::visit` instead, which provides the same feature. ([PR #656](https://github.com/boostorg/gil/pull/656)) - documentation: Display that GIL is a header-only library - Moved numeric extension to core ([PR #573](https://github.com/boostorg/gil/pull/573)) -- Added support for C++17's `` ([PR #636](https://github.com/boostorg/gil/pull/636)). +- Added support for C++17's `` ([PR #636](https://github.com/boostorg/gil/pull/636)) The availability of the ``std::filesystem`` is detected automatically, unless the `BOOST_GIL_IO_USE_BOOST_FILESYSTEM` macro is defined that forces the preference of the Boost.Filesystem. - Renamed `pixel_multiply_t` to `pixel_multiplies_t` and `pixel_divide_t` to `pixel_divides_t`([PR #655](https://github.com/boostorg/gil/pull/655)) +- Renamed `io/dynamic_io_new.hpp` to `io/detail/dynamic.hpp` ([PR #653](https://github.com/boostorg/gil/pull/653)) +- Moved function `construct_method` into `boost::gil::detail` namespace as it was only used by other implementation details ([PR #653](https://github.com/boostorg/gil/pull/653)) +- Made `packed_pixel` trivially copyable and assignable ([PR #679](https://github.com/boostorg/gil/pull/679)) +- Replace deprecated libtiff v4.3 typedefs with C99 fixed-size integers ([#685](https://github.com/boostorg/gil/pull/685)) ### Removed - BREAKING: Removed support for GCC 5 ([PR #572](https://github.com/boostorg/gil/pull/572)) - Removed deprecated.hpp ([PR #627](https://github.com/boostorg/gil/pull/627)) ### Fixed +- Fixed conversion from RGB to HSL ([PR #505](https://github.com/boostorg/gil/pull/505)) - Fixed conversion from RGB to signed CMYK ([PR #522](https://github.com/boostorg/gil/pull/522)) - Removed unnecessary numeric cast in hsv.hpp ([PR #530](https://github.com/boostorg/gil/pull/530)) - Fixed default constructor for `homogeneous_color_base` for reference pixel elements ([PR #542](https://github.com/boostorg/gil/pull/542)) @@ -56,12 +62,13 @@ NOTICE: We are planning BREAKING switch to C++17 as minimum required C++ languag - Fixed `convolve_2d` for images with `float32_t` channel model ([PR #577](https://github.com/boostorg/gil/pull/577)) - Fixed `for_each_pixel` for non-1d iterable views ([PR #621](https://github.com/boostorg/gil/pull/621)) - Fixed: `is_equal_to_sixteen` in PNG I/O was less-than test ([PR #650](https://github.com/boostorg/gil/pull/650)) -- Re-allow `devicen_t` with two components ([PR #654](https://github.com/boostorg/gil/pull/654)). - It was unintentionally removed in Boost 1.72. +- Re-allow `devicen_t` with two components ([PR #654](https://github.com/boostorg/gil/pull/654)) + It was unintentionally removed in Boost 1.72 +- Fixed memory leak in `image` class for empty dimensions ([PR #649](https://github.com/boostorg/gil/pull/649)) ### Acknowledgements -Samuel Debionne, Nicolas Herry, Gaurav Kumar, Marco Langer, Pranam Lashkari, Mateusz Łoskot, Debabrata Mandal, Felix Morgner, Harshit Pant, Dirk Stolle, Prathamesh Tagore, Olzhas Zhumabek +Cypre55, Samuel Debionne, Mike-Devel, Edward Diener, Peter Dimov, Omar Emara, Dhruva Gole, Nicolas Herry, Eugene K, Avinal Kumar, Gaurav Kumar, Marco Langer, Pranam Lashkari, Mateusz Łoskot, Giovanni Mascellani, Debabrata Mandal, Gopi Krishna Menon, René Ferdinand Rivera Morell, Felix Morgner, Harshit Pant, Paul92, André Schröder, Scramjet911, Siddharth, Dirk Stolle, Prathamesh Tagore, theroyn, Olzhas Zhumabek ## [1.75.0] - 2020-12-09 From 4d80bc881fc1f128c9c73c90c0021844a5c088f0 Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Thu, 21 Jul 2022 23:47:05 +0200 Subject: [PATCH 160/193] build: Remove unused Boost.Build imports from example/Jamfile --- example/Jamfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/example/Jamfile b/example/Jamfile index 3b9de5a2a6..68afe04cb0 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -6,9 +6,7 @@ # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) -import ac ; import regex ; -import testing ; using libjpeg : : : : true ; # work around bug on master using libpng : : : : true ; From f52cc35f6a790dcc10194e43a8a3b2f9ccc860bf Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Thu, 21 Jul 2022 23:47:35 +0200 Subject: [PATCH 161/193] chore: Tidy up --- example/Jamfile | 1 - 1 file changed, 1 deletion(-) diff --git a/example/Jamfile b/example/Jamfile index 68afe04cb0..192fe9cad0 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -15,7 +15,6 @@ project : # requirements ; -# TODO: Add missing examples local sources = adaptive_histogram_equalization.cpp From 6dc55c6bfcbf17e545f319a0e76ad4141c8eec9e Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Thu, 21 Jul 2022 23:47:57 +0200 Subject: [PATCH 162/193] build: Add cxx14_constexpr requirement to example/Jamfile --- example/Jamfile | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/example/Jamfile b/example/Jamfile index 192fe9cad0..ba4a6e81c2 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -1,20 +1,37 @@ # Boost.GIL (Generic Image Library) - examples # -# Copyright (c) 2018 Mateusz Loskot +# Copyright (c) 2018-2022 Mateusz Loskot # # Use, modification and distribution is 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) +import ../../config/checks/config : requires ; import regex ; using libjpeg : : : : true ; # work around bug on master using libpng : : : : true ; -project - : # requirements - ; +# The header providing std::experimental::filesystem +# is deprecated by Microsoft and will be REMOVED. It is superseded by the C++17 +# header providing std::filesystem. +# You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING +# to acknowledge that you have received this warning. +local msvc-cxxs-with-experimental-fs = 14 ; +project + : + requirements + [ requires + cxx14_constexpr + cxx14_return_type_deduction + ] + . + # TODO: Enable concepts check for all, not just test/core + #BOOST_GIL_USE_CONCEPT_CHECK=1 + msvc,$(msvc-cxxs-with-experimental-fs):_SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING=1 + msvc:/bigobj + ; local sources = adaptive_histogram_equalization.cpp From e5e636c15471ab64b7a39a33dfa90d1e0fa7c97a Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sat, 23 Jul 2022 20:21:28 +0200 Subject: [PATCH 163/193] fix: Broken build of test project for gcc-5 (#712) Fixes #709 Fixes #710 It may also fix #711, we're yet to see after next run of Boost regression builds. --- test/extension/toolbox/color_convert_hsl.cpp | 16 +++++----- test/extension/toolbox/color_convert_rgb.cpp | 32 ++++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/extension/toolbox/color_convert_hsl.cpp b/test/extension/toolbox/color_convert_hsl.cpp index 299691842f..8ab46c84ab 100644 --- a/test/extension/toolbox/color_convert_hsl.cpp +++ b/test/extension/toolbox/color_convert_hsl.cpp @@ -35,14 +35,14 @@ void test_colors_hsl_to_rgb() { using color_t = std::tuple; std::vector colors = { - {0, 0, 0, 0, 0, 0}, // black - {0, 0, 1, 255, 255, 255}, // white - {0, 1, 0.5, 255, 0, 0}, // red - {2 / 6.f, 1, 0.5, 0, 255, 0}, // lime - {4 / 6.f, 1, 0.5, 0, 0, 255}, // blue - {5 / 6.f, 1, 0.25, 128, 0, 128}, // purple - {3 / 6.f, 1, 0.25, 0, 128, 128}, // teal - {4 / 6.f, 1, 0.25, 0, 0, 128}, // navy + color_t{0, 0, 0, 0, 0, 0}, // black + color_t{0, 0, 1, 255, 255, 255}, // white + color_t{0, 1, 0.5, 255, 0, 0}, // red + color_t{2 / 6.f, 1, 0.5, 0, 255, 0}, // lime + color_t{4 / 6.f, 1, 0.5, 0, 0, 255}, // blue + color_t{5 / 6.f, 1, 0.25, 128, 0, 128}, // purple + color_t{3 / 6.f, 1, 0.25, 0, 128, 128}, // teal + color_t{4 / 6.f, 1, 0.25, 0, 0, 128}, // navy }; for (auto&& color : colors) diff --git a/test/extension/toolbox/color_convert_rgb.cpp b/test/extension/toolbox/color_convert_rgb.cpp index cb22727a1c..90967a3612 100644 --- a/test/extension/toolbox/color_convert_rgb.cpp +++ b/test/extension/toolbox/color_convert_rgb.cpp @@ -35,22 +35,22 @@ void test_colors_rgb_to_hsl() { using color_t = std::tuple; std::vector colors = { - {0, 0, 0, 0, 0, 0}, // black - {255, 255, 255, 0, 0, 1}, // white - {255, 0, 0, 0, 1, 0.5}, // red - {0, 255, 0, 2 / 6.f, 1, 0.5}, // lime - {0, 0, 255, 4 / 6.f, 1, 0.5}, // blue - {255, 255, 0, 1 / 6.f, 1, 0.5}, // yellow - {0, 255, 255, 3 / 6.f, 1, 0.5}, // cyan - {255, 0, 255, 5 / 6.f, 1, 0.5}, // magenta - {191, 191, 191, 0, 0, 0.75}, // silver - {128, 128, 128, 0, 0, 0.5}, // gray - {128, 0, 0, 0, 1, 0.25}, // maroon - {128, 128, 0, 1 / 6.f, 1, 0.25}, // olive - {0, 128, 0, 2 / 6.f, 1, 0.25}, // green - {128, 0, 128, 5 / 6.f, 1, 0.25}, // purple - {0, 128, 128, 3 / 6.f, 1, 0.25}, // teal - {0, 0, 128, 4 / 6.f, 1, 0.25}, // navy + color_t{0, 0, 0, 0, 0, 0}, // black + color_t{255, 255, 255, 0, 0, 1}, // white + color_t{255, 0, 0, 0, 1, 0.5}, // red + color_t{0, 255, 0, 2 / 6.f, 1, 0.5}, // lime + color_t{0, 0, 255, 4 / 6.f, 1, 0.5}, // blue + color_t{255, 255, 0, 1 / 6.f, 1, 0.5}, // yellow + color_t{0, 255, 255, 3 / 6.f, 1, 0.5}, // cyan + color_t{255, 0, 255, 5 / 6.f, 1, 0.5}, // magenta + color_t{191, 191, 191, 0, 0, 0.75}, // silver + color_t{128, 128, 128, 0, 0, 0.5}, // gray + color_t{128, 0, 0, 0, 1, 0.25}, // maroon + color_t{128, 128, 0, 1 / 6.f, 1, 0.25}, // olive + color_t{0, 128, 0, 2 / 6.f, 1, 0.25}, // green + color_t{128, 0, 128, 5 / 6.f, 1, 0.25}, // purple + color_t{0, 128, 128, 3 / 6.f, 1, 0.25}, // teal + color_t{0, 0, 128, 4 / 6.f, 1, 0.25}, // navy }; for (auto&& color : colors) From b02149ad59d05400bd6c4b464ff931afa46b28bc Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Tue, 2 Aug 2022 09:03:28 +0200 Subject: [PATCH 164/193] docs: fix typo in release notes for #653 (#714) That typo was already fixed for the Boost website in commit , but the change has not made it back to GIL - yet. [ci skip] --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index f658df133b..da984ef640 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -41,7 +41,7 @@ NOTICE: We are planning BREAKING switch to C++17 as minimum required C++ languag the preference of the Boost.Filesystem. - Renamed `pixel_multiply_t` to `pixel_multiplies_t` and `pixel_divide_t` to `pixel_divides_t`([PR #655](https://github.com/boostorg/gil/pull/655)) - Renamed `io/dynamic_io_new.hpp` to `io/detail/dynamic.hpp` ([PR #653](https://github.com/boostorg/gil/pull/653)) -- Moved function `construct_method` into `boost::gil::detail` namespace as it was only used by other implementation details ([PR #653](https://github.com/boostorg/gil/pull/653)) +- Moved function `construct_matched` into `boost::gil::detail` namespace as it was only used by other implementation details ([PR #653](https://github.com/boostorg/gil/pull/653)) - Made `packed_pixel` trivially copyable and assignable ([PR #679](https://github.com/boostorg/gil/pull/679)) - Replace deprecated libtiff v4.3 typedefs with C99 fixed-size integers ([#685](https://github.com/boostorg/gil/pull/685)) From 8465d5463168a507fdd16d600b62c3efa51269fd Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Wed, 10 Aug 2022 18:37:20 +0200 Subject: [PATCH 165/193] ci: partially replace Ubuntu 18.04 on GHA with Ubuntu 20.04 (#716) GitHub started the deprecation process for Ubuntu 18.04 Action runners on 2022-08-08, and Ubuntu 18.04 will be completely unsupported by 2022-12-01 (if things proceed as planned). See for the announcement and more information on that. So this commit replace those Ubuntu 18.04 runners with the next newest Ubuntu 20.04 runner that have shown to still work with the newer version. The other five GHA jobs with Ubuntu 18.04 (gcc-6, gcc-7, clang++-3.9, clang++-4.0, clang++-5.0) still need to be dealt with separately later. --- .github/workflows/ci.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6cf10bd9e1..a49372079c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,14 +28,16 @@ jobs: - toolset: gcc compiler: g++-8 cxxstd: "14" - os: ubuntu-18.04 + os: ubuntu-20.04 install: g++-8 - toolset: gcc-9 cxxstd: "14,17" - os: ubuntu-18.04 + os: ubuntu-20.04 + install: g++-9 - toolset: gcc-10 cxxstd: "14,17" - os: ubuntu-18.04 + os: ubuntu-20.04 + install: g++-10 - toolset: clang compiler: clang++-3.9 cxxstd: "14" @@ -54,12 +56,12 @@ jobs: - toolset: clang compiler: clang++-6.0 cxxstd: "14,17" - os: ubuntu-18.04 + os: ubuntu-20.04 install: clang-6.0 - toolset: clang compiler: clang++-7 cxxstd: "14,17" - os: ubuntu-18.04 + os: ubuntu-20.04 install: clang-7 - toolset: clang compiler: clang++-8 From 1f89d622e3755d472232591ee5a341564af85d91 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Fri, 12 Aug 2022 09:21:13 +0200 Subject: [PATCH 166/193] ci: replace macOS 10.15 with macOS 11 in CI pipelines (#717) * ci: replace macOS 10.15 with macOS 11 in CI workflow GitHub started the deprecation process for macOS 10.15 runners on 2022-05-31, and macOS 10.15 will be completely unsupported by 2022-08-30 (if things proceed as planned). See for more information on the removal of the macOS 10.15 images. This issue already hit us during the build for PR #716, because at that time there was a scheduled brownout for macOS 10.15 builds to raise awareness of the upcoming removal. * ci: remove Azure pipeline build configuration See . --- .azure-pipelines.yml | 71 ------------------- .ci/azure-pipelines/steps-check-cmake.yml | 9 --- .../steps-cmake-build-and-test.yml | 51 ------------- .ci/azure-pipelines/steps-install-boost.yml | 41 ----------- .ci/azure-pipelines/steps-install-conan.yml | 17 ----- .ci/azure-pipelines/steps-install-gcc.yml | 18 ----- .github/workflows/ci.yml | 2 +- README.md | 8 +-- 8 files changed, 5 insertions(+), 212 deletions(-) delete mode 100644 .azure-pipelines.yml delete mode 100644 .ci/azure-pipelines/steps-check-cmake.yml delete mode 100644 .ci/azure-pipelines/steps-cmake-build-and-test.yml delete mode 100644 .ci/azure-pipelines/steps-install-boost.yml delete mode 100644 .ci/azure-pipelines/steps-install-conan.yml delete mode 100644 .ci/azure-pipelines/steps-install-gcc.yml diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml deleted file mode 100644 index 0a5e394f5d..0000000000 --- a/.azure-pipelines.yml +++ /dev/null @@ -1,71 +0,0 @@ -# Azure Pipelines for Boost.GIL -# -# Copyright 2018-2019 Mateusz Loskot -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) -# -variables: - #system.debug: true - configuration: release - -trigger: - - master - - develop - - azure-pipelines - - ml/* - -jobs: - - job: 'ubuntu1804_gcc6_cxx14_cmake' - pool: - vmImage: 'ubuntu-18.04' - steps: - - template: .ci/azure-pipelines/steps-install-gcc.yml - parameters: - major_version: '6' - - script: which g++ && g++ --version - displayName: 'Check GCC' - - template: .ci/azure-pipelines/steps-check-cmake.yml - - script: | - sudo -E apt-get update - sudo -E apt-get -yq --no-install-suggests --no-install-recommends install libpng-dev libjpeg-dev libtiff5-dev libraw-dev - displayName: 'Install dependencies' - - template: .ci/azure-pipelines/steps-install-boost.yml - - template: .ci/azure-pipelines/steps-cmake-build-and-test.yml - parameters: - cxxver: '14' - - - job: 'ubuntu1804_gcc8_cxx14_cmake' - pool: - vmImage: 'ubuntu-18.04' - steps: - - template: .ci/azure-pipelines/steps-install-gcc.yml - parameters: - major_version: '8' - - script: which g++ && g++ --version - displayName: 'Check GCC' - - template: .ci/azure-pipelines/steps-check-cmake.yml - - script: | - sudo -E apt-get update - sudo -E apt-get -yq --no-install-suggests --no-install-recommends install libpng-dev libjpeg-dev libtiff5-dev libraw-dev - displayName: 'Install dependencies' - - template: .ci/azure-pipelines/steps-install-boost.yml - - template: .ci/azure-pipelines/steps-cmake-build-and-test.yml - parameters: - cxxver: '14' - - - job: 'macos1015_xcode11_cmake' - pool: - vmImage: 'macOS-10.15' - steps: - - script: which clang++ && clang++ --version - displayName: 'Check clang' - - template: .ci/azure-pipelines/steps-check-cmake.yml - - template: .ci/azure-pipelines/steps-install-conan.yml - parameters: - python: python3 - - template: .ci/azure-pipelines/steps-install-boost.yml - parameters: - toolset: darwin - - template: .ci/azure-pipelines/steps-cmake-build-and-test.yml - parameters: - use_conan: 'ON' diff --git a/.ci/azure-pipelines/steps-check-cmake.yml b/.ci/azure-pipelines/steps-check-cmake.yml deleted file mode 100644 index 8b4cd9109a..0000000000 --- a/.ci/azure-pipelines/steps-check-cmake.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Azure Pipelines for Boost.GIL -# -# Copyright 2018-2019 Mateusz Loskot -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) -# -steps: - - script: cmake --version - displayName: 'Check CMake' diff --git a/.ci/azure-pipelines/steps-cmake-build-and-test.yml b/.ci/azure-pipelines/steps-cmake-build-and-test.yml deleted file mode 100644 index a4454faaf0..0000000000 --- a/.ci/azure-pipelines/steps-cmake-build-and-test.yml +++ /dev/null @@ -1,51 +0,0 @@ -# Azure Pipelines for Boost.GIL -# -# Copyright 2018-2019 Mateusz Loskot -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) -# -parameters: - # defaults, if not specified - configuration: 'Release' - cxxver: '14' - enable_ext_io: 'OFF' - enable_ext_numeric: 'ON' - enable_ext_toolbox: 'ON' - use_conan: 'OFF' - -steps: - - script: | - export BOOST_ROOT=$(Build.SourcesDirectory)/boost-root - export BOOST_INCLUDEDIR=$BOOST_ROOT - export BOOST_LIBRARYDIR=$BOOST_ROOT/lib - cmake -H. -B_build -DCMAKE_BUILD_TYPE=${{ parameters.configuration }} -DCMAKE_CXX_STANDARD=${{ parameters.cxxver }} -DCMAKE_VERBOSE_MAKEFILE=ON -DBoost_DEBUG=ON -DBoost_NO_SYSTEM_PATHS=ON -DBOOST_GIL_USE_CONAN=${{ parameters.use_conan }} -DBOOST_GIL_ENABLE_EXT_IO=${{ parameters.enable_ext_io }} -DBOOST_GIL_ENABLE_EXT_NUMERIC=${{ parameters.enable_ext_numeric }} -DBOOST_GIL_ENABLE_EXT_TOOLBOX=${{ parameters.enable_ext_toolbox }} - workingDirectory: $(Build.SourcesDirectory)/boost-root/libs/gil - displayName: 'Run CMake to configure build on Unix' - condition: ne(variables['Agent.OS'], 'Windows_NT') - - - script: | - set BOOST_ROOT=$(Build.SourcesDirectory)\boost-root - set BOOST_INCLUDEDIR=%BOOST_ROOT% - set BOOST_LIBRARYDIR=%BOOST_ROOT%\lib - cmake -H. -B_build -DCMAKE_BUILD_TYPE=${{ parameters.configuration }} -DCMAKE_CXX_STANDARD=${{ parameters.cxxver }} -DCMAKE_VERBOSE_MAKEFILE=ON -DBoost_DEBUG=ON -DBoost_ADDITIONAL_VERSIONS="1.70;1.71" -DBoost_ARCHITECTURE=-x32 -DBoost_NO_SYSTEM_PATHS=ON -DBOOST_GIL_USE_CONAN=${{ parameters.use_conan }} -DBOOST_GIL_ENABLE_EXT_IO=${{ parameters.enable_ext_io }} -DBOOST_GIL_ENABLE_EXT_NUMERIC=${{ parameters.enable_ext_numeric }} -DBOOST_GIL_ENABLE_EXT_TOOLBOX=${{ parameters.enable_ext_toolbox }} - workingDirectory: $(Build.SourcesDirectory)/boost-root/libs/gil - displayName: 'Run CMake to configure build on Windows' - condition: eq(variables['Agent.OS'], 'Windows_NT') - - - script: cmake --build _build --config ${{ parameters.configuration }} -j 4 - workingDirectory: $(Build.SourcesDirectory)/boost-root/libs/gil - displayName: 'Run CMake to build' - - - script: cd _build && ctest -V --output-on-failure --build-config ${{ parameters.configuration }} - workingDirectory: $(Build.SourcesDirectory)/boost-root/libs/gil - displayName: 'Run CTest to test' - condition: ne(variables['Agent.OS'], 'Darwin') - - - script: | - # Set DYLD_FALLBACK_LIBRARY_PATH to avoid 'dyld: Library not loaded: libboost_*.dylib' error - export DYLD_FALLBACK_LIBRARY_PATH=$(Build.SourcesDirectory)/boost-root/stage/lib:$DYLD_FALLBACK_LIBRARY_PATH - echo "DYLD_FALLBACK_LIBRARY_PATH=$DYLD_FALLBACK_LIBRARY_PATH" - cd _build && ctest -V --output-on-failure --build-config ${{ parameters.configuration }} - workingDirectory: $(Build.SourcesDirectory)/boost-root/libs/gil - displayName: 'Run CTest to test on macOS' - condition: eq(variables['Agent.OS'], 'Darwin') diff --git a/.ci/azure-pipelines/steps-install-boost.yml b/.ci/azure-pipelines/steps-install-boost.yml deleted file mode 100644 index f921737306..0000000000 --- a/.ci/azure-pipelines/steps-install-boost.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Azure Pipelines for Boost.GIL -# -# Copyright 2018-2019 Mateusz Loskot -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) -# -parameters: - # defaults, if not specified - variant: 'release' - toolset: 'gcc' - -steps: - # The $BSD_DIR is the same path as $(Build.SourcesDirectory) - # but guaranteed in Unix format /a/b/c on every OS. - - bash: | - export BSD_DIR=$PWD - cd .. - export GIL_DIR=$PWD/gil - echo "Copying $BSD_DIR to $GIL_DIR" - cp -a $BSD_DIR $GIL_DIR - cd $BSD_DIR - $GIL_DIR/.ci/get-boost.sh $(Build.SourceBranchName) $GIL_DIR - displayName: 'Download Boost' - - - script: | - cd boost-root - ./bootstrap.sh - sudo ./b2 headers - sudo ./b2 -j4 toolset=${{ parameters.toolset }} variant=${{ parameters.variant }} --with-filesystem stage - ls stage/lib - displayName: 'Install Boost on Unix' - condition: ne(variables['Agent.OS'], 'Windows_NT') - - - script: | - cd boost-root - call .\bootstrap.bat - .\b2 headers - .\b2 -j4 address-model=32 variant=${{ parameters.variant }} --layout=system --with-filesystem stage - dir stage\lib - displayName: 'Install Boost on Windows' - condition: eq(variables['Agent.OS'], 'Windows_NT') diff --git a/.ci/azure-pipelines/steps-install-conan.yml b/.ci/azure-pipelines/steps-install-conan.yml deleted file mode 100644 index 70507a4bb6..0000000000 --- a/.ci/azure-pipelines/steps-install-conan.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Azure Pipelines for Boost.GIL -# -# Copyright 2018-2019 Mateusz Loskot -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) -# -parameters: - python: 'python' # default, if not specified - -steps: - - script: | - ${{ parameters.python }} --version - ${{ parameters.python }} -m ensurepip - ${{ parameters.python }} -m pip install --upgrade pip - ${{ parameters.python }} -m pip install --upgrade conan - conan --version - displayName: 'Install Conan' diff --git a/.ci/azure-pipelines/steps-install-gcc.yml b/.ci/azure-pipelines/steps-install-gcc.yml deleted file mode 100644 index 2f11eb5d81..0000000000 --- a/.ci/azure-pipelines/steps-install-gcc.yml +++ /dev/null @@ -1,18 +0,0 @@ -# Azure Pipelines for Boost.GIL -# -# Copyright 2019 Mateusz Loskot -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) -# -parameters: - # defaults, if not specified - major_version: 8 - -steps: - - bash: | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test - sudo apt-get update -qq - sudo apt-get install g++-${{ parameters.major_version }} - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${{ parameters.major_version }} 90 - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ parameters.major_version }} 90 - displayName: 'Install GCC' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a49372079c..9d4760daee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,7 +79,7 @@ jobs: os: ubuntu-20.04 - toolset: clang cxxstd: "14,17" - os: macos-10.15 + os: macos-11 runs-on: ${{matrix.os}} diff --git a/README.md b/README.md index d71fe15a1f..b46d70a57d 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://bintray.com/bincrafters/public-conan/boost_gil%3Abincrafters) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://github.com/Microsoft/vcpkg/tree/master/ports/boost-gil) -Documentation | GitHub Actions | AppVeyor | Azure Pipelines | Regression | Codecov ---------------|----------------|----------|-----------------|------------|---------- -[![develop](https://img.shields.io/badge/doc-develop-blue.svg)](https://boostorg.github.io/gil/develop/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/gil/actions?query=branch:develop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h/branch/develop?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/develop) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=develop)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=develop) | [![gil](https://img.shields.io/badge/gil-develop-blue.svg)](http://www.boost.org/development/tests/develop/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/develop) -[![master](https://img.shields.io/badge/doc-master-blue.svg)](https://boostorg.github.io/gil/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=master)](https://github.com/boostorg/gil/actions?query=branch:master) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/master) | [![Azure](https://dev.azure.com/boostorg/gil/_apis/build/status/boostorg.gil?branchName=master)](https://dev.azure.com/boostorg/gil/_build/latest?definitionId=7&branchName=master) | [![gil](https://img.shields.io/badge/gil-master-blue.svg)](http://www.boost.org/development/tests/master/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/master) +Documentation | GitHub Actions | AppVeyor | Regression | Codecov +--------------|----------------|----------|------------|---------- +[![develop](https://img.shields.io/badge/doc-develop-blue.svg)](https://boostorg.github.io/gil/develop/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/gil/actions?query=branch:develop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h/branch/develop?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/develop) | [![gil](https://img.shields.io/badge/gil-develop-blue.svg)](http://www.boost.org/development/tests/develop/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/develop) +[![master](https://img.shields.io/badge/doc-master-blue.svg)](https://boostorg.github.io/gil/) | [![GitHub Actions](https://github.com/boostorg/gil/workflows/CI/badge.svg?branch=master)](https://github.com/boostorg/gil/actions?query=branch:master) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w4k19d8io2af168h?svg=true)](https://ci.appveyor.com/project/stefanseefeld/gil/branch/master) | [![gil](https://img.shields.io/badge/gil-master-blue.svg)](http://www.boost.org/development/tests/master/developer/gil.html) | [![codecov](https://codecov.io/gh/boostorg/gil/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/boostorg/gil/branch/master) # Boost.GIL From 135edcfad336c226ab3341c5384283043dcd6606 Mon Sep 17 00:00:00 2001 From: Sam Darwin Date: Wed, 5 Oct 2022 03:16:45 -0600 Subject: [PATCH 167/193] docs: Support newer version of Sphinx (#719) --- doc/_static/boost.css | 19 +++++++++++++++++++ doc/_templates/layout.html | 3 +++ 2 files changed, 22 insertions(+) diff --git a/doc/_static/boost.css b/doc/_static/boost.css index 28f8935991..582f421eb7 100644 --- a/doc/_static/boost.css +++ b/doc/_static/boost.css @@ -714,3 +714,22 @@ span.purple { color: purple; } span.gold { color: gold; } span.silver { color: silver; } /* lighter gray */ span.gray { color: #808080; } /* light gray */ + +/* 2022 fix */ + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html index 6be6d55e28..9aada3d2aa 100644 --- a/doc/_templates/layout.html +++ b/doc/_templates/layout.html @@ -49,6 +49,9 @@ {%- for scriptfile in script_files %} {%- endfor %} + + + {%- if use_opensearch %} Date: Thu, 6 Oct 2022 22:49:45 +0200 Subject: [PATCH 168/193] docs: Fix HTML syntax Patch via e-mail from @sdarwin to refine PR #719 Thank you Sam! --- doc/_templates/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html index 9aada3d2aa..17281c7352 100644 --- a/doc/_templates/layout.html +++ b/doc/_templates/layout.html @@ -51,7 +51,7 @@ {%- endfor %} - + {%- if use_opensearch %} Date: Mon, 14 Nov 2022 11:15:10 +0100 Subject: [PATCH 169/193] ci: Update actions/checkout in GitHub Actions workflows to v3 (#720) --- .github/workflows/ci.yml | 4 ++-- .github/workflows/coverage.yml | 3 +-- .github/workflows/docs.yaml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d4760daee..0f8d379dc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,7 +84,7 @@ jobs: runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install packages if: matrix.install @@ -142,7 +142,7 @@ jobs: runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Boost shell: cmd diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 3bd330aba2..a21a27549c 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -15,7 +15,7 @@ jobs: coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: install packages @@ -52,4 +52,3 @@ jobs: uses: codecov/codecov-action@v1.2.1 with: files: ../boost-root/coverage.info - \ No newline at end of file diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 9a20e7c55f..1308258f91 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -9,7 +9,7 @@ jobs: build-docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ./.github/actions/docs-prerequisites - uses: ./.github/actions/setup-boost - uses: ./.github/actions/generate-doc From 712b827edbe60a1eb6c13b1a677e7d2a4f74a116 Mon Sep 17 00:00:00 2001 From: Christoph Gringmuth Date: Tue, 7 Feb 2023 14:10:38 +0100 Subject: [PATCH 170/193] fix: Convolution in convolve_2d (#723) Fixes #722 --- include/boost/gil/detail/math.hpp | 2 +- .../boost/gil/image_processing/convolve.hpp | 2 +- test/core/image_processing/convolve_2d.cpp | 93 +++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/include/boost/gil/detail/math.hpp b/include/boost/gil/detail/math.hpp index 700136edcb..5515adb2e8 100644 --- a/include/boost/gil/detail/math.hpp +++ b/include/boost/gil/detail/math.hpp @@ -15,7 +15,7 @@ namespace boost { namespace gil { namespace detail { static constexpr double pi = 3.14159265358979323846; -static constexpr std::array dx_sobel = {{-1, 0, 1, -2, 0, 2, -1, 0, 1}}; +static constexpr std::array dx_sobel = {{1, 0, -1, 2, 0, -2, 1, 0, -1}}; static constexpr std::array dx_scharr = {{-1, 0, 1, -1, 0, 1, -1, 0, 1}}; static constexpr std::array dy_sobel = {{1, 2, 1, 0, 0, 0, -1, -2, -1}}; static constexpr std::array dy_scharr = {{1, 1, 1, 0, 0, 0, -1, -1, -1}}; diff --git a/include/boost/gil/image_processing/convolve.hpp b/include/boost/gil/image_processing/convolve.hpp index a6369c89a6..91384f1626 100644 --- a/include/boost/gil/image_processing/convolve.hpp +++ b/include/boost/gil/image_processing/convolve.hpp @@ -400,7 +400,7 @@ void convolve_2d_impl(SrcView const& src_view, DstView const& dst_view, Kernel c { aux_total += src_view(col_boundary, row_boundary)[0] * - kernel.at(flip_ker_row, flip_ker_col); + kernel.at(flip_ker_col, flip_ker_row); } } } diff --git a/test/core/image_processing/convolve_2d.cpp b/test/core/image_processing/convolve_2d.cpp index 3b37b8e37b..cb6a96f66e 100644 --- a/test/core/image_processing/convolve_2d.cpp +++ b/test/core/image_processing/convolve_2d.cpp @@ -100,9 +100,102 @@ gil::detail::convolve_2d(src_view, kernel, view(img_gray_out)); BOOST_TEST(gil::equal_pixels(exp_out_view, view(img_gray_out))); } +void test_convolve_2d_with_sobel_x_filter() +{ + const gil::uint8_t img[] = + { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + const gil::int16_t exp_output[] = + { + 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, 0, + 0, 255, 255, 0, 0, -255, -255, 0, 0, 0, + 0, 765, 765, 0, 0, -765, -765, 0, 0, 0, + 0, 1020, 1020, 0, 0, -1020, -1020, 0, 0, 0, + 0, 1020, 1020, 0, 0, -1020, -1020, 0, 0, 0, + 0, 1020, 1020, 0, 0, -1020, -1020, 0, 0, 0, + 0, 765, 765, 0, 0, -765, -765, 0, 0, 0, + 0, 255, 255, 0, 0, -255, -255, 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, 0, 0, 0, 0 + }; + + gil::gray16s_image_t img_gray_out(10, 13); + const auto src_view = + gil::interleaved_view(10, 13, reinterpret_cast(img), 10); + const auto exp_out_view = + gil::interleaved_view(10, 13, reinterpret_cast(exp_output), 20); + gil::detail::convolve_2d(src_view, gil::generate_dx_sobel(1), view(img_gray_out)); + + BOOST_TEST(gil::equal_pixels(exp_out_view, view(img_gray_out))); +} + +void test_convolve_2d_with_sobel_y_filter() +{ + const gil::uint8_t img[] = + { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + const gil::int16_t exp_output[] = + { + 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, 0, + 0, 255, 765, 1020, 1020, 765, 255, 0, 0, 0, + 0, 255, 765, 1020, 1020, 765, 255, 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, 0, 0, 0, 0, + 0, -255, -765, -1020, -1020, -765, -255, 0, 0, 0, + 0, -255, -765, -1020, -1020, -765, -255, 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, 0, 0, 0, 0 + }; + + gil::gray16s_image_t img_gray_out(10, 13); + const auto src_view = + gil::interleaved_view(10, 13, reinterpret_cast(img), 10); + const auto exp_out_view = + gil::interleaved_view(10, 13, reinterpret_cast(exp_output), 20); + gil::detail::convolve_2d(src_view, gil::generate_dy_sobel(1), view(img_gray_out)); + BOOST_TEST(gil::equal_pixels(exp_out_view, view(img_gray_out))); +} + int main() { test_convolve_2d_with_normalized_mean_filter(); test_convolve_2d_with_image_using_float32_t(); + test_convolve_2d_with_sobel_x_filter(); + test_convolve_2d_with_sobel_y_filter(); return ::boost::report_errors(); } From d6e67f38c548372e3d5c4e656c2d763c914bf918 Mon Sep 17 00:00:00 2001 From: Christoph Gringmuth Date: Tue, 7 Feb 2023 14:11:51 +0100 Subject: [PATCH 171/193] fix: Normalize Gaussian 2D kernel. (#725) --- include/boost/gil/image_processing/numeric.hpp | 15 ++++++++++----- test/core/image_processing/simple_kernels.cpp | 17 ++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/boost/gil/image_processing/numeric.hpp b/include/boost/gil/image_processing/numeric.hpp index 8507e0e294..5500c7a96e 100644 --- a/include/boost/gil/image_processing/numeric.hpp +++ b/include/boost/gil/image_processing/numeric.hpp @@ -136,21 +136,26 @@ inline auto generate_gaussian_kernel(std::size_t side_length, double sigma) throw std::invalid_argument("kernel dimensions should be odd and equal"); const double denominator = 2 * boost::gil::detail::pi * sigma * sigma; - auto middle = side_length / 2; + auto const middle = side_length / 2; std::vector values(side_length * side_length); + T sum{0}; for (std::size_t y = 0; y < side_length; ++y) { for (std::size_t x = 0; x < side_length; ++x) { - const auto delta_x = middle > x ? middle - x : x - middle; - const auto delta_y = middle > y ? middle - y : y - middle; - const double power = (delta_x * delta_x + delta_y * delta_y) / (2 * sigma * sigma); + const auto delta_x = x - middle; + const auto delta_y = y - middle; + const auto power = static_cast(delta_x * delta_x + delta_y * delta_y) / (2 * sigma * sigma); const double nominator = std::exp(-power); - const float value = static_cast(nominator / denominator); + const auto value = static_cast(nominator / denominator); values[y * side_length + x] = value; + sum += value; } } + // normalize so that Gaussian kernel sums up to 1. + std::transform(values.begin(), values.end(), values.begin(), [&sum](const auto & v) { return v/sum; }); + return detail::kernel_2d(values.begin(), values.size(), middle, middle); } diff --git a/test/core/image_processing/simple_kernels.cpp b/test/core/image_processing/simple_kernels.cpp index 522dec2f16..311de88e4b 100644 --- a/test/core/image_processing/simple_kernels.cpp +++ b/test/core/image_processing/simple_kernels.cpp @@ -38,15 +38,16 @@ void test_gaussian_kernel_generation() auto kernel = boost::gil::generate_gaussian_kernel(7, 0.84089642); const float expected_values[7][7] = { - {0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f}, - {0.00002292f, 0.00078633f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f}, - {0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f}, - {0.00038771f, 0.01330373f, 0.11098164f, 0.25508352f, 0.11098164f, 0.01330373f, 0.00038711f}, - {0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f}, - {0.00002292f, 0.00078633f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f}, - {0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f} + {6.67847706e-07f, 2.29160778e-05f, 1.91169232e-04f, 3.87713181e-04f, 1.91169232e-04f, 2.29160778e-05f, 6.67847706e-07f}, + {2.29160778e-05f, 7.86326905e-04f, 6.55965268e-03f, 1.33037298e-02f, 6.55965268e-03f, 7.86326905e-04f, 2.29160778e-05f}, + {1.91169232e-04f, 6.55965268e-03f, 5.47215706e-02f, 1.10981636e-01f, 5.47215706e-02f, 6.55965268e-03f, 1.91169232e-04f}, + {3.87713181e-04f, 1.33037298e-02f, 1.10981636e-01f, 2.25083518e-01f, 1.10981636e-01f, 1.33037298e-02f, 3.87713181e-04f}, + {1.91169232e-04f, 6.55965268e-03f, 5.47215706e-02f, 1.10981636e-01f, 5.47215706e-02f, 6.55965268e-03f, 1.91169232e-04f}, + {2.29160778e-05f, 7.86326905e-04f, 6.55965268e-03f, 1.33037298e-02f, 6.55965268e-03f, 7.86326905e-04f, 2.29160778e-05f}, + {6.67847706e-07f, 2.29160778e-05f, 1.91169232e-04f, 3.87713181e-04f, 1.91169232e-04f, 2.29160778e-05f, 6.67847706e-07f} }; + double sum{0}; for (gil::gray32f_view_t::coord_t y = 0; static_cast(y) < kernel.size(); ++y) { for (gil::gray32f_view_t::coord_t x = 0; static_cast(x) < kernel.size(); ++x) @@ -55,8 +56,10 @@ void test_gaussian_kernel_generation() auto expected = expected_values[y][x]; auto percent_difference = std::ceil(std::abs(expected - output) / expected); BOOST_TEST_LT(percent_difference, 5); + sum += output; } } + BOOST_TEST_LT(std::abs(sum-1), 1e-7); } int main() From 8f4cdcbcce2be1cec30fad9bcf375ca3d8eee1b7 Mon Sep 17 00:00:00 2001 From: nicolacandussi <72563215+nicolacandussi@users.noreply.github.com> Date: Tue, 21 Feb 2023 10:38:03 -0300 Subject: [PATCH 172/193] fix: Fixed custom color converter in dynamic_factory and added corresponding (#726) --- .../dynamic_image/image_view_factory.hpp | 20 ++---- .../dynamic_image/image_view_factory.cpp | 67 +++++++++++++++++++ 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp index 6667a828fa..a41dc32c54 100644 --- a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp +++ b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp @@ -314,20 +314,12 @@ auto nth_channel_view(any_image_view const& src, int n) namespace detail { -template -struct get_ccv_type : color_converted_view_type {}; - template struct views_get_ccv_type { private: - // FIXME: Remove class name injection with detail:: qualification - // Required as workaround for MP11 issue that treats unqualified metafunction - // in the class definition of the same name as the specialization (Peter Dimov): - // invalid template argument for template parameter 'F', expected a class template - template - using ccvt = detail::get_ccv_type; - + template + using ccvt = typename color_converted_view_type::type; public: using type = mp11::mp_transform; }; @@ -340,7 +332,7 @@ template struct color_converted_view_type,DstP,CC> { //using type = any_image_view::type>; - using type = detail::views_get_ccv_type, DstP, CC>; + using type = typename detail::views_get_ccv_type, DstP, CC>::type; }; /// \ingroup ImageViewTransformationsColorConvert @@ -348,11 +340,11 @@ struct color_converted_view_type,DstP,CC> /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto color_converted_view(any_image_view const& src, CC) +auto color_converted_view(any_image_view const& src, CC cc) -> typename color_converted_view_type, DstP, CC>::type { using cc_view_t = typename color_converted_view_type, DstP, CC>::type; - return variant2::visit(detail::color_converted_view_fn(), src); + return variant2::visit(detail::color_converted_view_fn(cc), src); } /// \ingroup ImageViewTransformationsColorConvert @@ -360,7 +352,7 @@ auto color_converted_view(any_image_view const& src, CC) template struct color_converted_view_type,DstP> { - using type = detail::views_get_ccv_type, DstP, default_color_converter>; + using type = typename detail::views_get_ccv_type, DstP, default_color_converter>::type; }; /// \ingroup ImageViewTransformationsColorConvert diff --git a/test/extension/dynamic_image/image_view_factory.cpp b/test/extension/dynamic_image/image_view_factory.cpp index ce0ad9ee28..3837f740e5 100644 --- a/test/extension/dynamic_image/image_view_factory.cpp +++ b/test/extension/dynamic_image/image_view_factory.cpp @@ -83,10 +83,77 @@ struct test_flipped_up_down_view } }; +template +bool equal_pixels_values(V1&& v1, + V2&& v2, + float threshold = 1e-6f) +{ +// convert both images to rgba32f and compare with threshold + return boost::variant2::visit([=](auto const& v1, auto const& v2) -> bool { + auto it1 = v1.begin(); + auto it2 = v2.begin(); + while(it1 != v1.end() && it2 != v2.end()) { + using pixel_t = gil::rgba32f_pixel_t; + static constexpr std::size_t num_channels = gil::num_channels::value; + pixel_t p1{}; + gil::color_convert(*it1++, p1); + pixel_t p2{}; + gil::color_convert(*it2++, p2); + for(size_t i = 0; i < num_channels; ++i) { + if(std::abs(p1[i] - p2[i]) > threshold){ + return false; + } + } + } + return true; + }, + std::forward(v1), + std::forward(v2)); +} + +struct test_color_converted_view +{ + static void run() + { + using dynamic_image_t = gil::any_image; + using src_image_t = gil::gray8_image_t; + using dst_image_t = gil::gray16_image_t; + using dst_pixel_t = typename dst_image_t::value_type; + static constexpr std::size_t num_channels = 1; + auto color_converter = [](auto const& src, auto& dst) { dst = 2 * src; }; + std::array pixel_data = + { + 0, 1, 2, + 3, 4, 5, + 6, 7, 8 + }; + std::array expected_pixel_data; + std::transform(std::begin(pixel_data), + std::end(pixel_data), + std::begin(expected_pixel_data), + [](auto v) { return 2 * v; }); + + dynamic_image_t source_image = + fixture::generate_image( + 3, 3, generator{pixel_data.data()}); + + dynamic_image_t expected_image = + fixture::generate_image( + 3, 3, generator{expected_pixel_data.data()}); + + auto result_view = gil::color_converted_view(gil::const_view(source_image), color_converter); + + BOOST_TEST(equal_pixels_values(result_view, + gil::const_view(expected_image), + 1e-6f)); + } +}; int main() { test_flipped_up_down_view::run(); + test_color_converted_view::run(); + return ::boost::report_errors(); } From ecea33a9051e607dd05db199fdbe6ccdb31a1a5a Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 22 Feb 2023 11:59:12 +0100 Subject: [PATCH 173/193] fix: Make locator.hpp self contained (#729) --- include/boost/gil/locator.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/boost/gil/locator.hpp b/include/boost/gil/locator.hpp index 776eedd336..f0e7c20366 100644 --- a/include/boost/gil/locator.hpp +++ b/include/boost/gil/locator.hpp @@ -8,8 +8,7 @@ #ifndef BOOST_GIL_LOCATOR_HPP #define BOOST_GIL_LOCATOR_HPP -#include -#include +#include #include #include From eabd679f632be3170d98145fcc3b49e85df7cc4b Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Thu, 16 Mar 2023 21:08:04 +0100 Subject: [PATCH 174/193] fix: Documentation (#731) --- doc/design/dynamic_image.rst | 6 +++--- doc/io.rst | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/design/dynamic_image.rst b/doc/design/dynamic_image.rst index 546bfa4d9f..ec9b01e3fb 100644 --- a/doc/design/dynamic_image.rst +++ b/doc/design/dynamic_image.rst @@ -159,14 +159,14 @@ upside down: .. code-block:: cpp - #include + #include template // Could be rgb8_image_t or any_image<...> void save_180rot(const std::string& file_name) { Image img; - jpeg_read_image(file_name, img); - jpeg_write_view(file_name, rotated180_view(view(img))); + read_image(file_name, img, jpeg_tag()); + write_view(file_name, rotated180_view(view(img)), jpeg_tag()); } It can be instantiated with either a compile-time or a runtime image diff --git a/doc/io.rst b/doc/io.rst index 01462dbc97..6f47371675 100644 --- a/doc/io.rst +++ b/doc/io.rst @@ -172,6 +172,8 @@ provides access to the so-called backend. For instance:: + typedef bmp_tag tag_t; + typedef get_reader_backend< const std::string , tag_t >::type backend_t; @@ -245,7 +247,7 @@ The following example shows this feature:: , gray16_image_t , rgb8_image_t , rgba8_image_t - > my_img_types > runtime_image; + > runtime_image; read_image( filename , runtime_image @@ -323,7 +325,7 @@ Writing an any_image<...> is supported. See the following example:: , gray16_image_t , rgb8_image_t , rgba8_image_t - > my_img_types > runtime_image; + > runtime_image; // fill any_image From 2fec9b0d61d174b3ebc7c1f59cc18f96bfd1a9c1 Mon Sep 17 00:00:00 2001 From: olzhas Date: Sat, 11 May 2019 02:24:30 +0600 Subject: [PATCH 175/193] add benchmark configuration This commit adds support for benchmark definition and running, currently supporting only cmake. Configuration was tested using GIL_USE_CONAN on, on Windows. One benchmark set was run successfully --- CMakeLists.txt | 5 + benchmark/CMakeLists.txt | 72 ++++++++ benchmark/README.txt | 8 + benchmark/pixel_channel_arithmetics.cpp | 232 ++++++++++++++++++++++++ doc/_static/boost.css | 19 -- 5 files changed, 317 insertions(+), 19 deletions(-) create mode 100644 benchmark/CMakeLists.txt create mode 100644 benchmark/README.txt create mode 100644 benchmark/pixel_channel_arithmetics.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fd94577e89..ce76d8734e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ cmake_minimum_required(VERSION 3.10) #----------------------------------------------------------------------------- # Options #----------------------------------------------------------------------------- +option(BOOST_GIL_BUILD_BENCHMARKS "Builds all available benchmarks in the benchmark directory" OFF) option(BOOST_GIL_BUILD_EXAMPLES "Build examples" ON) option(BOOST_GIL_BUILD_HEADER_TESTS "Enable self-contained header tests" ON) option(BOOST_GIL_ENABLE_EXT_DYNAMIC_IMAGE "Enable Dynamic Image extension, tests and examples" ON) @@ -288,4 +289,8 @@ if(BOOST_GIL_BUILD_EXAMPLES AND BOOST_GIL_ENABLE_EXT_IO) add_subdirectory(example) endif() +if(GIL_BUILD_BENCHMARKS) + add_subdirectory(benchmark) +endif() + endif() diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt new file mode 100644 index 0000000000..f6b068b4a3 --- /dev/null +++ b/benchmark/CMakeLists.txt @@ -0,0 +1,72 @@ +add_library(benchmark_dependencies INTERFACE) + +if(WIN32) + find_library(SHLWAPI Shlwapi.lib) + target_link_libraries(benchmark_dependencies INTERFACE SHLWAPI) +endif() + +if (GIL_USE_CONAN) + # Download automatically, you can also just copy the conan.cmake file + if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") + message(STATUS "Boost.GIL: Downloading conan.cmake from https://github.com/conan-io/cmake-conan") + file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/v0.13/conan.cmake" + "${CMAKE_BINARY_DIR}/conan.cmake") + endif() + + # NOTE: See RelWithDebInfo for Release builds, http://docs.conan.io/en/latest/howtos/vs2017_cmake.html + set(_build_type_saved ${CMAKE_BUILD_TYPE}) + if(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + set(CMAKE_BUILD_TYPE "Release") + endif() + + include(${CMAKE_BINARY_DIR}/conan.cmake) + conan_cmake_run(REQUIRES google-benchmark/1.4.1@mpusz/stable BASIC_SETUP CMAKE_TARGETS) + + set(CMAKE_BUILD_TYPE ${_build_type_saved}) + unset(_build_type_saved) + + target_link_libraries(benchmark_dependencies + INTERFACE + CONAN_PKG::google-benchmark) +else() + find_package(benchmark REQUIRED) + target_link_libraries(gil_dependencies benchmark) +endif() + +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) +else() + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) +endif() + +foreach(_benchmark ${_benchmarks}) + get_filename_component(_name ${_benchmark} NAME_WE) + add_executable(benchmark_${_name} ${_name}.cpp) + target_compile_definitions(benchmark_${_name} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1) + # Unfortunately, ALIAS of imported target is not supported + # see https://github.com/conan-io/conan/issues/2125 + if(GIL_USE_CONAN) + target_link_libraries(benchmark_${_name} + PRIVATE + gil_compile_options + gil_include_directories + Boost::disable_autolinking + Boost::filesystem + CONAN_PKG::libjpeg + CONAN_PKG::libpng + CONAN_PKG::libtiff + benchmark_dependencies) + else() + target_link_libraries(benchmark_${_name} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies + benchmark_dependencies) + endif() + + unset(_name) +endforeach() + +unset(_benchmarks) +unset(_benchmark) diff --git a/benchmark/README.txt b/benchmark/README.txt new file mode 100644 index 0000000000..726c962b19 --- /dev/null +++ b/benchmark/README.txt @@ -0,0 +1,8 @@ +This is general guidelines on building the benchmarks. + +1. google-benchmark should be installed and searchable by the build tool of + your choice. +2. If the library is not installed and GIL_USE_CONAN is specified, users need + to have the following repo specified in the remote: + conan-mpusz: https://api.bintray.com/conan/mpusz/conan-mpusz +3. BUILD IN RELEASE MODE! diff --git a/benchmark/pixel_channel_arithmetics.cpp b/benchmark/pixel_channel_arithmetics.cpp new file mode 100644 index 0000000000..c2d5d1fb38 --- /dev/null +++ b/benchmark/pixel_channel_arithmetics.cpp @@ -0,0 +1,232 @@ +#include +#include +#include + +#include + +#include +#include + +using namespace boost::gil; + +static void std_algo(benchmark::State& state) +{ + std::vector img(1024 * 1024, 0); + + for (auto _ : state) + // The code to benchmark + for (auto&& p : img) + p += 1; + + if (!std::all_of(img.begin(), img.end(), [&state](auto p) { return p == state.iterations(); })) + state.SkipWithError("std_algo wrong result"); +} +BENCHMARK(std_algo); + +static void gil_algo_stdfunction(benchmark::State& state) +{ + using namespace std::placeholders; + namespace gil = boost::gil; + + gil::gray16_image_t img(1024, 1024, gray16_pixel_t{ 0 }); + + //Pixel operation + auto op = std::bind( + gil::pixel_plus_t(), + _1, gray16_pixel_t{ 1 }); + + for (auto _ : state) + // The code to benchmark + gil::transform_pixels(gil::const_view(img), gil::view(img), op); + + if (!std::all_of(gil::view(img).begin(), gil::view(img).end(), [&state](auto p) { return p == state.iterations(); })) + state.SkipWithError("gil_algo_stdfunction wrong result"); +} +BENCHMARK(gil_algo_stdfunction); + +static void gil_algo_lambda(benchmark::State& state) +{ + using namespace std::placeholders; + namespace gil = boost::gil; + + gil::gray16_image_t img(1024, 1024, gray16_pixel_t{ 0 }); + + //Pixel operation + auto op2 = [second_arg = gray16_pixel_t{ 1 }](gray16_pixel_t value) { + return gil::pixel_plus_t()(value, second_arg); + }; + + for (auto _ : state) + // The code to benchmark + gil::transform_pixels(gil::const_view(img), gil::view(img), op2); + + if (!std::all_of(gil::view(img).begin(), gil::view(img).end(), [&state](auto p) { return p == state.iterations(); })) + state.SkipWithError("gil_algo_lambda wrong result"); +} +BENCHMARK(gil_algo_lambda); + +static void gil_algo_lambda_plus(benchmark::State& state) +{ + using namespace std::placeholders; + namespace gil = boost::gil; + + gil::gray16_image_t img(1024, 1024, gray16_pixel_t{ 0 }); + + //Pixel operation + auto op2 = [second_arg = gray16_pixel_t{ 1 }](gray16_pixel_t value) { + return value + second_arg; + }; + + for (auto _ : state) + // The code to benchmark + gil::transform_pixels(gil::const_view(img), gil::view(img), op2); + + if (!std::all_of(gil::view(img).begin(), gil::view(img).end(), [&state](auto p) { return p == state.iterations(); })) + state.SkipWithError("gil_algo_lambda_plus wrong result"); +} +BENCHMARK(gil_algo_lambda_plus); + +static void gil_algo_lambda_copy(benchmark::State& state) +{ + using namespace std::placeholders; + namespace gil = boost::gil; + + gil::gray16_image_t img(1024, 1024, gray16_pixel_t{ 0 }); + gil::gray16_image_t img2(1024, 1024, gray16_pixel_t{ 0 }); + + auto input_view = gil::const_view(img); + auto output_view = gil::view(img2); + auto plus = gil::pixel_plus_t{}; + + //Pixel operation + auto op2 = [second_arg = gray16_pixel_t{ 1 }](gray16_pixel_t value) { + return gil::pixel_plus_t()(value, second_arg); + }; + + for (auto _ : state) + // The code to benchmark + gil::transform_pixels(input_view, output_view, op2); + + if (!std::all_of(output_view.begin(), output_view.end(), [&state](auto p) { return p == 1; })) + state.SkipWithError("gil_algo_lambda_copy wrong result"); +} +BENCHMARK(gil_algo_lambda_copy); + +static void gil_algo_rangefor(benchmark::State& state) { + using namespace std::placeholders; + namespace gil = boost::gil; + + gil::gray16_image_t img(1024, 1024, gray16_pixel_t{ 0 }); + gil::gray16_image_t img2(1024, 1024, gray16_pixel_t{ 0 }); + + auto input_view = gil::const_view(img); + auto output_view = gil::view(img2); + auto plus = gil::pixel_plus_t{}; + + //Pixel operation + auto op2 = [second_arg = gray16_pixel_t{ 1 }](gray16_pixel_t value) { + return gil::pixel_plus_t()(value, second_arg); + }; + + for (auto _ : state) { + auto d_first = output_view.begin(); + for (auto pixel : input_view) { + *d_first++ = op2(pixel); + } + } + + if (!std::all_of(output_view.begin(), output_view.end(), [&state](auto p) { return p == 1; })) + state.SkipWithError("gil_algo_rangefor wrong result"); +} +BENCHMARK(gil_algo_rangefor); + +static void gil_algo_rangefor_plus(benchmark::State& state) { + using namespace std::placeholders; + namespace gil = boost::gil; + + gil::gray16_image_t img(1024, 1024, gray16_pixel_t{ 0 }); + gil::gray16_image_t img2(1024, 1024, gray16_pixel_t{ 0 }); + + auto input_view = gil::const_view(img); + auto output_view = gil::view(img2); + auto plus = gil::pixel_plus_t{}; + + //Pixel operation + auto op2 = [second_arg = gray16_pixel_t{ 1 }](gray16_pixel_t value) { + return gil::pixel_plus_t()(value, second_arg); + }; + + for (auto _ : state) { + auto d_first = output_view.begin(); + for (const auto& pixel : input_view) { + *d_first++ = pixel + 1; + } + } + + if (!std::all_of(output_view.begin(), output_view.end(), [&state](auto p) { return p == 1; })) + state.SkipWithError("gil_algo_rangefor_plus wrong result"); +} +BENCHMARK(gil_algo_rangefor_plus); + +static void gil_algo_copy_mem(benchmark::State& state) { + using namespace std::placeholders; + namespace gil = boost::gil; + + gil::gray16_image_t img(1024, 1024, gray16_pixel_t{ 0 }); + gil::gray16_image_t img2(1024, 1024, gray16_pixel_t{ 0 }); + + auto input_view = gil::const_view(img); + auto output_view = gil::view(img2); + + //Pixel operation + auto op2 = [second_arg = gray16_pixel_t{ 1 }](gray16_pixel_t value) { + return gil::pixel_plus_t()(value, second_arg); + }; + + for (auto _ : state) { + auto start = std::addressof(input_view(0, 0)); + auto end = std::addressof(input_view(input_view.width() - 1, input_view.height() - 1)) + 1; + auto d_start = std::addressof(output_view(0, 0)); + while (start != end) { + *d_start++ = *start++ + 1; + } + } + + if (!std::all_of(output_view.begin(), output_view.end(), [&state](auto p) { return p == 1; })) + state.SkipWithError("gil_algo_copy_mem wrong result"); +} +BENCHMARK(gil_algo_copy_mem); + +static void gil_algo_pixel_vector(benchmark::State& state) { + using namespace std::placeholders; + namespace gil = boost::gil; + std::vector img(1024 * 1024, gil::gray16_pixel_t(0)); + + for (auto _ : state) { + for (auto& pixel : img) + pixel.at(std::integral_constant{}) = pixel.at(std::integral_constant{}) + 1; + } + + if (!std::all_of(img.begin(), img.end(), [&state](auto p) {return p == state.iterations(); })) + state.SkipWithError("gil_algo_pixel_vector wrong result"); +} +BENCHMARK(gil_algo_pixel_vector); + +static void gil_algo_pixel_vector_reinterpret(benchmark::State& state) { + using namespace std::placeholders; + namespace gil = boost::gil; + std::vector img(1024 * 1024, gil::gray16_pixel_t(0)); + + for (auto _ : state) { + auto start = reinterpret_cast(img.data()); + auto end = start + img.size(); + for (; start != end; ++start) + *start += 1; + } + + if (!std::all_of(img.begin(), img.end(), [&state](auto p) {return p == state.iterations(); })) + state.SkipWithError("gil_algo_pixel_vector wrong result"); +} +BENCHMARK(gil_algo_pixel_vector_reinterpret); + +BENCHMARK_MAIN(); diff --git a/doc/_static/boost.css b/doc/_static/boost.css index 582f421eb7..28f8935991 100644 --- a/doc/_static/boost.css +++ b/doc/_static/boost.css @@ -714,22 +714,3 @@ span.purple { color: purple; } span.gold { color: gold; } span.silver { color: silver; } /* lighter gray */ span.gray { color: #808080; } /* light gray */ - -/* 2022 fix */ - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} From 395f020c56d28608adc20e0c2ef66f2c63d1ed54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sun, 12 May 2019 10:59:29 +0200 Subject: [PATCH 176/193] Refactor CMakeLists.txt for benchmarks --- CMakeLists.txt | 11 +++++++---- benchmark/CMakeLists.txt | 21 ++------------------- conanfile.txt | 3 +++ 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ce76d8734e..95f26fda6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -282,6 +282,13 @@ if(BUILD_TESTING) add_subdirectory(test) endif() +#----------------------------------------------------------------------------- +# Benchmarks +#----------------------------------------------------------------------------- +if(GIL_BUILD_BENCHMARKS) + add_subdirectory(benchmark) +endif() + #----------------------------------------------------------------------------- # Examples #----------------------------------------------------------------------------- @@ -289,8 +296,4 @@ if(BOOST_GIL_BUILD_EXAMPLES AND BOOST_GIL_ENABLE_EXT_IO) add_subdirectory(example) endif() -if(GIL_BUILD_BENCHMARKS) - add_subdirectory(benchmark) -endif() - endif() diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index f6b068b4a3..4d733cb5f5 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -1,3 +1,5 @@ +message(STATUS "Boost.GIL: Configuring benchmarks") + add_library(benchmark_dependencies INTERFACE) if(WIN32) @@ -6,25 +8,6 @@ if(WIN32) endif() if (GIL_USE_CONAN) - # Download automatically, you can also just copy the conan.cmake file - if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") - message(STATUS "Boost.GIL: Downloading conan.cmake from https://github.com/conan-io/cmake-conan") - file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/v0.13/conan.cmake" - "${CMAKE_BINARY_DIR}/conan.cmake") - endif() - - # NOTE: See RelWithDebInfo for Release builds, http://docs.conan.io/en/latest/howtos/vs2017_cmake.html - set(_build_type_saved ${CMAKE_BUILD_TYPE}) - if(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - set(CMAKE_BUILD_TYPE "Release") - endif() - - include(${CMAKE_BINARY_DIR}/conan.cmake) - conan_cmake_run(REQUIRES google-benchmark/1.4.1@mpusz/stable BASIC_SETUP CMAKE_TARGETS) - - set(CMAKE_BUILD_TYPE ${_build_type_saved}) - unset(_build_type_saved) - target_link_libraries(benchmark_dependencies INTERFACE CONAN_PKG::google-benchmark) diff --git a/conanfile.txt b/conanfile.txt index 90916e500e..6054312db0 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -5,6 +5,9 @@ # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # +[build_requires] +google-benchmark/1.4.1@mpusz/stable + [requires] libjpeg/9d libpng/1.6.37 From b7520b7dc0767e7aecc57a2bd357ed0998a1d48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sun, 12 May 2019 13:49:22 +0200 Subject: [PATCH 177/193] [conna] Move google-benchmark to [requires] --- conanfile.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/conanfile.txt b/conanfile.txt index 6054312db0..df8fb185d5 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -5,14 +5,15 @@ # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # -[build_requires] -google-benchmark/1.4.1@mpusz/stable - [requires] libjpeg/9d libpng/1.6.37 libtiff/4.1.0 +# Add required remote: +# conan remote add conan-mpusz https://api.bintray.com/conan/mpusz/conan-mpusz +google-benchmark/1.4.1@mpusz/stable + [generators] CMakeDeps CMakeToolchain From de4e1827b63c04334e18cc94ccb450e05bfb7fc8 Mon Sep 17 00:00:00 2001 From: olzhas Date: Tue, 14 May 2019 15:46:41 +0600 Subject: [PATCH 178/193] add celero and demo celero benchmark The commit adds celero into conanfile and cmakelists, also provides one incorrectly implemented benchmark, which shows results that are completely inconsistent with google-benchmark --- benchmark/CMakeLists.txt | 9 +- benchmark/README.txt | 1 + .../celero_pixel_channel_arithmetics.cpp | 96 +++++++++++++++++++ conanfile.txt | 6 ++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 benchmark/celero_pixel_channel_arithmetics.cpp diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 4d733cb5f5..967a8d8a51 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -10,10 +10,15 @@ endif() if (GIL_USE_CONAN) target_link_libraries(benchmark_dependencies INTERFACE - CONAN_PKG::google-benchmark) + CONAN_PKG::google-benchmark + CONAN_PKG::celero) else() find_package(benchmark REQUIRED) - target_link_libraries(gil_dependencies benchmark) + find_package(celero REQUIRED) + target_link_libraries(benchmark_dependencies + INTERFACE + benchmark + celero) endif() if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) diff --git a/benchmark/README.txt b/benchmark/README.txt index 726c962b19..165ff5aed6 100644 --- a/benchmark/README.txt +++ b/benchmark/README.txt @@ -5,4 +5,5 @@ This is general guidelines on building the benchmarks. 2. If the library is not installed and GIL_USE_CONAN is specified, users need to have the following repo specified in the remote: conan-mpusz: https://api.bintray.com/conan/mpusz/conan-mpusz + ppodsiadly: https://api.bintray.com/conan/ppodsiadly/conan 3. BUILD IN RELEASE MODE! diff --git a/benchmark/celero_pixel_channel_arithmetics.cpp b/benchmark/celero_pixel_channel_arithmetics.cpp new file mode 100644 index 0000000000..972a1d5098 --- /dev/null +++ b/benchmark/celero_pixel_channel_arithmetics.cpp @@ -0,0 +1,96 @@ +#include + +#include +#include + +#include +#include +#include + +namespace gil = boost::gil; + +class ChannelArithmeticsIntVectorFixture : public celero::TestFixture { +public: + std::vector img; + + ChannelArithmeticsIntVectorFixture() {} + + virtual std::vector getExperimentValues() const override { + std::vector problemSpace; + return { celero::TestFixture::ExperimentValue(1024 * 1024) }; + } + + virtual void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override { + img = std::vector(1024 * 1024, gil::gray16_pixel_t(0)); + } + + virtual void tearDown() override { + if (!std::all_of(img.begin(), img.end(), [](auto p) { return p > 0; })) + throw std::logic_error("doesn't calculate correctly"); + } +}; + +class ChannelArithmeticsPixelVectorFixture : public celero::TestFixture { +public: + std::vector img; + + ChannelArithmeticsPixelVectorFixture() {} + + virtual std::vector getExperimentValues() const override { + std::vector problemSpace; + return { celero::TestFixture::ExperimentValue(1024 * 1024) }; + } + + virtual void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override { + img = std::vector(1024 * 1024, gil::gray16_pixel_t(0)); + } + + virtual void tearDown() override { + if (!std::all_of(img.begin(), img.end(), [](auto p) { return p > 0; })) + throw std::logic_error("doesn't calculate correctly"); + } +}; + +class ChannelArithmeticsImageFixture : public celero::TestFixture { +public: + gil::gray16_image_t img; + ChannelArithmeticsImageFixture() {} + + virtual std::vector getExperimentValues() const override { + std::vector problemSpace; + return { celero::TestFixture::ExperimentValue(1024 * 1024) }; + } + + virtual void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override { + img = gil::gray16_image_t(1024 * 1024, gil::gray16_pixel_t(0)); + + } + + virtual void tearDown() override { + auto view = gil::view(img); + if (!std::all_of(view.begin(), view.end(), [](auto p) { return p > 0; })) + throw std::logic_error("doesn't calculate correctly"); + } +}; + +CELERO_MAIN + +BASELINE_F(RawMemory, RawMemoryVector, ChannelArithmeticsIntVectorFixture, 1, 1'000) { + for (auto&& p : img) + p += 1; +} + +BASELINE_F(GilImage, GilImageAlgorithm, ChannelArithmeticsImageFixture, 1, 1'000) { + auto op = std::bind( + gil::pixel_plus_t(), + std::placeholders::_1, gil::gray16_pixel_t{ 1 }); + gil::transform_pixels(gil::const_view(img), gil::view(img), op); +} + +BASELINE_F(GilPixelVector, PixelVectorReinterpret, ChannelArithmeticsPixelVectorFixture, 1, 1'000) { + auto start = reinterpret_cast(img.data()); + auto end = start + img.size(); + for (; start != end; ++start) + *start += 1; + +} diff --git a/conanfile.txt b/conanfile.txt index df8fb185d5..5795b2608d 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -14,6 +14,12 @@ libtiff/4.1.0 # conan remote add conan-mpusz https://api.bintray.com/conan/mpusz/conan-mpusz google-benchmark/1.4.1@mpusz/stable +# remote add ppodsiadly https://api.bintray.com/conan/ppodsiadly/conan +celero/2.4.0@ppodsiadly/stable + +[options] +celero:shared=False + [generators] CMakeDeps CMakeToolchain From 41c21563998c614b690106dcd9f492c51701329e Mon Sep 17 00:00:00 2001 From: olzhas Date: Sat, 18 May 2019 04:01:12 +0600 Subject: [PATCH 179/193] update README for benchmark folder This commit adds a guide on how to write, build and execute benchmarks with supported frameworks (google-benchmark, celero). It also changes celero benchmark to crash instead of outputting wrong results. --- benchmark/README.md | 120 ++++++++++++++++++ benchmark/README.txt | 9 -- .../celero_pixel_channel_arithmetics.cpp | 30 +++-- 3 files changed, 138 insertions(+), 21 deletions(-) create mode 100644 benchmark/README.md delete mode 100644 benchmark/README.txt diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 0000000000..b7c3835b6b --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,120 @@ +## Building GIL Benchmarks + +This guide assumes that minimal GIL can already be built, e.g. dependency on dependent Boost libraries are satisfied. + +--- + +### Benchmark dependencies + +#### Windows + +The easiest way to build on windows would be using `-DGIL_USE_CONAN` switch with CMake. This requires **conan** package manager to be installed ([Conan download page]([https://conan.io/downloads.html](https://conan.io/downloads.html))), either `RelWithDebInfo` or `Release` build type specified, and following remote repositories specified: + + conan remote add conan-mpusz https://api.bintray.com/conan/mpusz/conan-mpusz + conan remote add ppodsiadly https://api.bintray.com/conan/ppodsiadly/conan + +--- + +The next option is **vcpkg** ([link]([https://github.com/microsoft/vcpkg](https://github.com/microsoft/vcpkg))). `benchmark` and `celero` libraries need to be installed. Do note that triplet should be `x64-windows` if you are building on x64 machine. Then, run following command: + + vcpkg integrate install + +To get your toolchain file location (at the end of the output). Then pass the option during configuration of the project. + +--- + +#### Linux distributions: + +`-DGIL_USE_CONAN` applies for linux distros too. Do note that using `pip` for installing conan might be preferential in this case. + +--- + +For manually installing libraries, search using package manager installed on the system. Do note that `google-benchmark` is called `benchmark` in some repositories. + +--- + +### Writing benchmarks + +Both frameworks provide facilities to avoid optimizing away some portions of the code or effects on variables. It might get bypassed by compilers in the future though, as they get smarter. Thus, providing some side effect outside of benchmarked section is advised. + +--- + +#### Google benchmark + +Overall structure of the file should look roughly like this: + + + + BENCHMARK(function1_name) + BENCHMARK(function2_name) + ... + + BENCHMARK_MAIN() + +Lets have a closer look at a benchmarking function: + + void function_name(benchmark::State& state) { + + + for (auto _: state) { + + } + + } + +Do note that as long as `_` is in scope, the runtime will be counted towards benchmarking results, whereas the rest will not be included. Not writing a side effect (like a check can throw, or printing on the console) might result in code being optimized away, thus making whole benchmarking function useless. + +--- + +#### celero + +Writing a `celero` benchmark is a bit more involved process. As most benchmarks will require *some* setup code not to be counted in the benchmarking section, the guide ignores function only benchmarks. To provide setup code and side effects, one has to write a fixture: + + class FixtureName : public celero::TextFixture { + public: + + FixtureName() = default; //trivial default constructor + + virtual std::vector getExperimentValues() const override { + + } + + virtual void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override { + + } + + virtual void tearDown() override { + + + } + } + +Fixture is sort of data provider. It can be used to control what input will be generated, e.g. size, variety, data type. Do note that `celero::TestFixture::ExperimentValue` can be extended using inheritance, and in original form is just a combination of numerical value and number of iterations. + +Then, to specify code to be benchmarked, use the following: + + BASELINE_F(GroupName, BenchmarkName, FixtureName, SampleCount, IterationCount) { + + } + + BENCHMARK_F(GroupName, BenchmarkName, FixtureName, SampleCount, IterationCount) { + + } + + + + + +Baseline should be the code to be used as a reference: performance goal, original performance, etc. Other benchmarks should be competitors of the baseline. Do note that the code inside `BASELINE_F` and `BENCHMARK_F` will be timed, the rest will not. + +--- + +### Building the benchmarks + +To build a benchmark, simply put the benchmark `.cpp` file into `/benchmark` and reconfigure the project. Then, the targets to build are `benchmark_`. + +--- + +### Running benchmarks + +Pairing both benchmarking frameworks with a good plotting tool of your choice is advised. Both frameworks provide options to control which benchmarks to run, for how long, and output formats. Running `--help` on executables will print available options. diff --git a/benchmark/README.txt b/benchmark/README.txt deleted file mode 100644 index 165ff5aed6..0000000000 --- a/benchmark/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -This is general guidelines on building the benchmarks. - -1. google-benchmark should be installed and searchable by the build tool of - your choice. -2. If the library is not installed and GIL_USE_CONAN is specified, users need - to have the following repo specified in the remote: - conan-mpusz: https://api.bintray.com/conan/mpusz/conan-mpusz - ppodsiadly: https://api.bintray.com/conan/ppodsiadly/conan -3. BUILD IN RELEASE MODE! diff --git a/benchmark/celero_pixel_channel_arithmetics.cpp b/benchmark/celero_pixel_channel_arithmetics.cpp index 972a1d5098..fa81e1bd14 100644 --- a/benchmark/celero_pixel_channel_arithmetics.cpp +++ b/benchmark/celero_pixel_channel_arithmetics.cpp @@ -3,25 +3,27 @@ #include #include +#include #include #include #include namespace gil = boost::gil; +constexpr std::size_t imageSize = 1024 * 1024; + class ChannelArithmeticsIntVectorFixture : public celero::TestFixture { public: std::vector img; - ChannelArithmeticsIntVectorFixture() {} virtual std::vector getExperimentValues() const override { std::vector problemSpace; - return { celero::TestFixture::ExperimentValue(1024 * 1024) }; + return { celero::TestFixture::ExperimentValue(imageSize, 0) }; } virtual void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override { - img = std::vector(1024 * 1024, gil::gray16_pixel_t(0)); + img = std::vector(imageSize, gil::gray16_pixel_t(0)); } virtual void tearDown() override { @@ -38,11 +40,11 @@ class ChannelArithmeticsPixelVectorFixture : public celero::TestFixture { virtual std::vector getExperimentValues() const override { std::vector problemSpace; - return { celero::TestFixture::ExperimentValue(1024 * 1024) }; + return { celero::TestFixture::ExperimentValue(imageSize, 0) }; } virtual void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override { - img = std::vector(1024 * 1024, gil::gray16_pixel_t(0)); + img = std::vector(imageSize, gil::gray16_pixel_t(0)); } virtual void tearDown() override { @@ -58,11 +60,11 @@ class ChannelArithmeticsImageFixture : public celero::TestFixture { virtual std::vector getExperimentValues() const override { std::vector problemSpace; - return { celero::TestFixture::ExperimentValue(1024 * 1024) }; + return { celero::TestFixture::ExperimentValue(imageSize, 0) }; } virtual void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override { - img = gil::gray16_image_t(1024 * 1024, gil::gray16_pixel_t(0)); + img = gil::gray16_image_t(1024, 1024, gil::gray16_pixel_t{ 0 }); } @@ -73,24 +75,28 @@ class ChannelArithmeticsImageFixture : public celero::TestFixture { } }; -CELERO_MAIN - -BASELINE_F(RawMemory, RawMemoryVector, ChannelArithmeticsIntVectorFixture, 1, 1'000) { +BASELINE_F(RawMemory, RawMemoryVector, ChannelArithmeticsIntVectorFixture, 2, 1'000) { + std::cout << img.size() << '\n'; for (auto&& p : img) p += 1; } -BASELINE_F(GilImage, GilImageAlgorithm, ChannelArithmeticsImageFixture, 1, 1'000) { +BENCHMARK_F(GilImage, GilImageAlgorithm, ChannelArithmeticsImageFixture, 2, 1'000) { + std::cout << img.height() * img.width() << '\n'; + celero::DoNotOptimizeAway(img); auto op = std::bind( gil::pixel_plus_t(), std::placeholders::_1, gil::gray16_pixel_t{ 1 }); gil::transform_pixels(gil::const_view(img), gil::view(img), op); } -BASELINE_F(GilPixelVector, PixelVectorReinterpret, ChannelArithmeticsPixelVectorFixture, 1, 1'000) { +BENCHMARK_F(GilPixelVector, PixelVectorReinterpret, ChannelArithmeticsPixelVectorFixture, 2, 1'000) { + std::cout << img.size() << '\n'; auto start = reinterpret_cast(img.data()); auto end = start + img.size(); for (; start != end; ++start) *start += 1; } + +CELERO_MAIN From 153861cb6de581f6278c5e5080da852619d17684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Tue, 11 Jun 2019 21:40:35 +0200 Subject: [PATCH 180/193] Deploy Celero and Google Benchmark as external project instead of Conan This way we have a bit more control on release of dependencies what solves some annoying issues (e.g. Conan is missing latest Celero fixed for VS2019). --- CMakeLists.txt | 12 +-- benchmark/CMakeLists.txt | 62 +------------- benchmark/README.md | 80 +++++++++---------- benchmark/celero/CMakeLists.txt | 64 +++++++++++++++ .../pixel_channel_arithmetics.cpp} | 0 benchmark/google/CMakeLists.txt | 63 +++++++++++++++ .../pixel_channel_arithmetics.cpp | 0 conanfile.txt | 4 +- 8 files changed, 176 insertions(+), 109 deletions(-) create mode 100644 benchmark/celero/CMakeLists.txt rename benchmark/{celero_pixel_channel_arithmetics.cpp => celero/pixel_channel_arithmetics.cpp} (100%) create mode 100644 benchmark/google/CMakeLists.txt rename benchmark/{ => google}/pixel_channel_arithmetics.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 95f26fda6b..6c24809546 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,17 +283,17 @@ if(BUILD_TESTING) endif() #----------------------------------------------------------------------------- -# Benchmarks +# Examples #----------------------------------------------------------------------------- -if(GIL_BUILD_BENCHMARKS) - add_subdirectory(benchmark) +if(BOOST_GIL_BUILD_EXAMPLES AND BOOST_GIL_ENABLE_EXT_IO) + add_subdirectory(example) endif() #----------------------------------------------------------------------------- -# Examples +# Benchmarks #----------------------------------------------------------------------------- -if(BOOST_GIL_BUILD_EXAMPLES AND BOOST_GIL_ENABLE_EXT_IO) - add_subdirectory(example) +if(BOOST_GIL_BUILD_BENCHMARKS) + add_subdirectory(benchmark) endif() endif() diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 967a8d8a51..4752f9b3fd 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -1,60 +1,2 @@ -message(STATUS "Boost.GIL: Configuring benchmarks") - -add_library(benchmark_dependencies INTERFACE) - -if(WIN32) - find_library(SHLWAPI Shlwapi.lib) - target_link_libraries(benchmark_dependencies INTERFACE SHLWAPI) -endif() - -if (GIL_USE_CONAN) - target_link_libraries(benchmark_dependencies - INTERFACE - CONAN_PKG::google-benchmark - CONAN_PKG::celero) -else() - find_package(benchmark REQUIRED) - find_package(celero REQUIRED) - target_link_libraries(benchmark_dependencies - INTERFACE - benchmark - celero) -endif() - -if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) - file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) -else() - file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) -endif() - -foreach(_benchmark ${_benchmarks}) - get_filename_component(_name ${_benchmark} NAME_WE) - add_executable(benchmark_${_name} ${_name}.cpp) - target_compile_definitions(benchmark_${_name} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1) - # Unfortunately, ALIAS of imported target is not supported - # see https://github.com/conan-io/conan/issues/2125 - if(GIL_USE_CONAN) - target_link_libraries(benchmark_${_name} - PRIVATE - gil_compile_options - gil_include_directories - Boost::disable_autolinking - Boost::filesystem - CONAN_PKG::libjpeg - CONAN_PKG::libpng - CONAN_PKG::libtiff - benchmark_dependencies) - else() - target_link_libraries(benchmark_${_name} - PRIVATE - gil_compile_options - gil_include_directories - gil_dependencies - benchmark_dependencies) - endif() - - unset(_name) -endforeach() - -unset(_benchmarks) -unset(_benchmark) +add_subdirectory(celero) +add_subdirectory(google) diff --git a/benchmark/README.md b/benchmark/README.md index b7c3835b6b..6d2a233698 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,45 +1,28 @@ -## Building GIL Benchmarks +# GIL Benchmarks -This guide assumes that minimal GIL can already be built, e.g. dependency on dependent Boost libraries are satisfied. +This guide assumes that minimal GIL can already be built, e.g. dependency on +dependent Boost libraries are satisfied. ---- - -### Benchmark dependencies - -#### Windows - -The easiest way to build on windows would be using `-DGIL_USE_CONAN` switch with CMake. This requires **conan** package manager to be installed ([Conan download page]([https://conan.io/downloads.html](https://conan.io/downloads.html))), either `RelWithDebInfo` or `Release` build type specified, and following remote repositories specified: - - conan remote add conan-mpusz https://api.bintray.com/conan/mpusz/conan-mpusz - conan remote add ppodsiadly https://api.bintray.com/conan/ppodsiadly/conan - ---- - -The next option is **vcpkg** ([link]([https://github.com/microsoft/vcpkg](https://github.com/microsoft/vcpkg))). `benchmark` and `celero` libraries need to be installed. Do note that triplet should be `x64-windows` if you are building on x64 machine. Then, run following command: - - vcpkg integrate install +## Dependencies -To get your toolchain file location (at the end of the output). Then pass the option during configuration of the project. +The benchmarks depend on popular micro-benchmarking frameworks: ---- - -#### Linux distributions: +* https://github.com/DigitalInBlue/Celero +* https://github.com/google/benchmark -`-DGIL_USE_CONAN` applies for linux distros too. Do note that using `pip` for installing conan might be preferential in this case. +All are configured using +[ExternalProject_Add](https://cmake.org/cmake/help/latest/module/ExternalProject.html) +with custom targets that drive download, build and deployment of the dependencies. ---- +## Writing Benchmarks -For manually installing libraries, search using package manager installed on the system. Do note that `google-benchmark` is called `benchmark` in some repositories. +Both frameworks provide facilities to avoid optimizing away some portions of the code or +effects on variables. It might get bypassed by compilers in the future though, as they get +smarter. Thus, providing some side effect outside of benchmarked section is advised. --- -### Writing benchmarks - -Both frameworks provide facilities to avoid optimizing away some portions of the code or effects on variables. It might get bypassed by compilers in the future though, as they get smarter. Thus, providing some side effect outside of benchmarked section is advised. - ---- - -#### Google benchmark +### Using Google Benchmark Overall structure of the file should look roughly like this: @@ -55,26 +38,32 @@ Lets have a closer look at a benchmarking function: void function_name(benchmark::State& state) { - + for (auto _: state) { } } -Do note that as long as `_` is in scope, the runtime will be counted towards benchmarking results, whereas the rest will not be included. Not writing a side effect (like a check can throw, or printing on the console) might result in code being optimized away, thus making whole benchmarking function useless. +Do note that as long as `_` is in scope, the runtime will be counted towards benchmarking results, +whereas the rest will not be included. Not writing a side effect (like a check can throw, +or printing on the console) might result in code being optimized away, thus making whole +benchmarking function useless. --- -#### celero +### Using Celero -Writing a `celero` benchmark is a bit more involved process. As most benchmarks will require *some* setup code not to be counted in the benchmarking section, the guide ignores function only benchmarks. To provide setup code and side effects, one has to write a fixture: +Writing a Celero benchmark is a bit more involved process. +As most benchmarks will require *some* setup code not to be counted in the benchmarking section, +the guide ignores function only benchmarks. +To provide setup code and side effects, one has to write a fixture: class FixtureName : public celero::TextFixture { public: FixtureName() = default; //trivial default constructor - + virtual std::vector getExperimentValues() const override { } @@ -89,7 +78,10 @@ Writing a `celero` benchmark is a bit more involved process. As most benchmarks } } -Fixture is sort of data provider. It can be used to control what input will be generated, e.g. size, variety, data type. Do note that `celero::TestFixture::ExperimentValue` can be extended using inheritance, and in original form is just a combination of numerical value and number of iterations. +Fixture is sort of data provider. It can be used to control what input will be generated, +e.g. size, variety, data type. Do note that `celero::TestFixture::ExperimentValue` +can be extended using inheritance, and in original form is just a combination of +numerical value and number of iterations. Then, to specify code to be benchmarked, use the following: @@ -105,16 +97,22 @@ Then, to specify code to be benchmarked, use the following: -Baseline should be the code to be used as a reference: performance goal, original performance, etc. Other benchmarks should be competitors of the baseline. Do note that the code inside `BASELINE_F` and `BENCHMARK_F` will be timed, the rest will not. +Baseline should be the code to be used as a reference: performance goal, original performance, etc. +Other benchmarks should be competitors of the baseline. Do note that the code inside +`BASELINE_F` and `BENCHMARK_F` will be timed, the rest will not. --- ### Building the benchmarks -To build a benchmark, simply put the benchmark `.cpp` file into `/benchmark` and reconfigure the project. Then, the targets to build are `benchmark_`. +To build a benchmark, simply put the benchmark `.cpp` file into `/benchmark` +and reconfigure the project. +Then, the targets to build are `benchmark__`. --- ### Running benchmarks -Pairing both benchmarking frameworks with a good plotting tool of your choice is advised. Both frameworks provide options to control which benchmarks to run, for how long, and output formats. Running `--help` on executables will print available options. +Pairing both benchmarking frameworks with a good plotting tool of your choice is advised. +Both frameworks provide options to control which benchmarks to run, for how long, +and output formats. Running `--help` on executables will print available options. diff --git a/benchmark/celero/CMakeLists.txt b/benchmark/celero/CMakeLists.txt new file mode 100644 index 0000000000..685fb2a47a --- /dev/null +++ b/benchmark/celero/CMakeLists.txt @@ -0,0 +1,64 @@ +message(STATUS "Boost.GIL: Configuring benchmarks: celero") + +#----------------------------------------------------------------------------- +# Deploy Celero +#----------------------------------------------------------------------------- +include(ExternalProject) + +# NOTE for Ninja generator: +# Ninja generator will try to find external project library at the expected location. +# BUILD_BYPRODUCTS option is added to specify build output and satisfy dependencies. +ExternalProject_Add(project_celero + GIT_REPOSITORY https://github.com/DigitalInBlue/Celero.git + GIT_TAG "v2.5.0" + GIT_SHALLOW 1 + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/vendor/celero + TIMEOUT 10 + BUILD_BYPRODUCTS /lib/static/${CMAKE_STATIC_LIBRARY_PREFIX}celerod${CMAKE_STATIC_LIBRARY_SUFFIX} + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/vendor/celero + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCELERO_COMPILE_DYNAMIC_LIBRARIES:BOOL=OFF + -DCELERO_ENABLE_TESTS:BOOL=OFF + -DCELERO_ENABLE_EXPERIMENTS:BOOL=OFF + -DCELERO_ENABLE_FOLDERS:BOOL=OFF) + +ExternalProject_Get_Property(project_celero INSTALL_DIR) + +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) +add_library(gil_celero STATIC IMPORTED) +target_include_directories(gil_celero INTERFACE ${INSTALL_DIR}/include) +target_compile_definitions(gil_celero INTERFACE CELERO_STATIC=1) +set_property(TARGET gil_celero PROPERTY IMPORTED_LOCATION_DEBUG + "${INSTALL_DIR}/lib/static/${CMAKE_STATIC_LIBRARY_PREFIX}celerod${CMAKE_STATIC_LIBRARY_SUFFIX}") +set_property(TARGET gil_celero PROPERTY IMPORTED_LOCATION_RELEASE + "${INSTALL_DIR}/lib/static/${CMAKE_STATIC_LIBRARY_PREFIX}celero${CMAKE_STATIC_LIBRARY_SUFFIX}") + +if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + find_library(POWRPROF PowrProf.lib) + target_link_libraries(gil_celero INTERFACE POWRPROF) +endif() + +add_dependencies(gil_celero project_celero) + +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) +else() + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) +endif() + +foreach(_benchmark ${_benchmarks}) + get_filename_component(_name ${_benchmark} NAME_WE) + set(_target benchmark_celero_${_name}) + add_executable(${_target} ${_name}.cpp) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies + gil_celero) +endforeach() + +unset(_benchmarks) +unset(_benchmark) diff --git a/benchmark/celero_pixel_channel_arithmetics.cpp b/benchmark/celero/pixel_channel_arithmetics.cpp similarity index 100% rename from benchmark/celero_pixel_channel_arithmetics.cpp rename to benchmark/celero/pixel_channel_arithmetics.cpp diff --git a/benchmark/google/CMakeLists.txt b/benchmark/google/CMakeLists.txt new file mode 100644 index 0000000000..71896de43b --- /dev/null +++ b/benchmark/google/CMakeLists.txt @@ -0,0 +1,63 @@ +message(STATUS "Boost.GIL: Configuring benchmarks: google") + +#----------------------------------------------------------------------------- +# Deploy google/benchmark +#----------------------------------------------------------------------------- +include(ExternalProject) + +# NOTE for Ninja generator: +# Ninja generator will try to find external project library at the expected location. +# BUILD_BYPRODUCTS option is added to specify build output and satisfy dependencies. +ExternalProject_Add(project_googlebenchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG "v1.5.0" + GIT_SHALLOW 1 + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/vendor/benchmark + TIMEOUT 10 + BUILD_BYPRODUCTS /lib/${CMAKE_STATIC_LIBRARY_PREFIX}benchmark${CMAKE_STATIC_LIBRARY_SUFFIX} + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/vendor/benchmark + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DBENCHMARK_ENABLE_TESTING=OFF + UPDATE_COMMAND "" + TEST_COMMAND "") + +ExternalProject_Get_Property(project_googlebenchmark INSTALL_DIR) + +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) +add_library(gil_googlebenchmark STATIC IMPORTED) +target_include_directories(gil_googlebenchmark INTERFACE ${INSTALL_DIR}/include) +set_property(TARGET gil_googlebenchmark PROPERTY IMPORTED_LOCATION + "${INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}benchmark${CMAKE_STATIC_LIBRARY_SUFFIX}") + +if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + find_library(SHLWAPI Shlwapi.lib) + target_link_libraries(gil_googlebenchmark INTERFACE SHLWAPI) +endif() + +add_dependencies(gil_googlebenchmark project_googlebenchmark) + +#----------------------------------------------------------------------------- +# Build benchmarks +#----------------------------------------------------------------------------- +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) +else() + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) +endif() + +foreach(_benchmark ${_benchmarks}) + get_filename_component(_name ${_benchmark} NAME_WE) + set(_target benchmark_google_${_name}) + add_executable(${_target} ${_name}.cpp) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies + gil_googlebenchmark) +endforeach() + +unset(_benchmarks) +unset(_benchmark) diff --git a/benchmark/pixel_channel_arithmetics.cpp b/benchmark/google/pixel_channel_arithmetics.cpp similarity index 100% rename from benchmark/pixel_channel_arithmetics.cpp rename to benchmark/google/pixel_channel_arithmetics.cpp diff --git a/conanfile.txt b/conanfile.txt index 5795b2608d..62af46128b 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -12,10 +12,10 @@ libtiff/4.1.0 # Add required remote: # conan remote add conan-mpusz https://api.bintray.com/conan/mpusz/conan-mpusz -google-benchmark/1.4.1@mpusz/stable +#google-benchmark/1.4.1@mpusz/stable # remote add ppodsiadly https://api.bintray.com/conan/ppodsiadly/conan -celero/2.4.0@ppodsiadly/stable +#celero/2.4.0@ppodsiadly/stable [options] celero:shared=False From 90469f302e57db837818a47b94327a5c0d511d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Fri, 21 Jun 2019 21:01:57 +0200 Subject: [PATCH 181/193] Fix Celero library name for optimized build. --- benchmark/celero/CMakeLists.txt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/benchmark/celero/CMakeLists.txt b/benchmark/celero/CMakeLists.txt index 685fb2a47a..03f822d7ed 100644 --- a/benchmark/celero/CMakeLists.txt +++ b/benchmark/celero/CMakeLists.txt @@ -14,7 +14,9 @@ ExternalProject_Add(project_celero GIT_SHALLOW 1 PREFIX ${CMAKE_CURRENT_BINARY_DIR}/vendor/celero TIMEOUT 10 - BUILD_BYPRODUCTS /lib/static/${CMAKE_STATIC_LIBRARY_PREFIX}celerod${CMAKE_STATIC_LIBRARY_SUFFIX} + BUILD_BYPRODUCTS + /lib/static/${CMAKE_STATIC_LIBRARY_PREFIX}celerod${CMAKE_STATIC_LIBRARY_SUFFIX} + /lib/static/${CMAKE_STATIC_LIBRARY_PREFIX}celero${CMAKE_DEBUG_POSTFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/vendor/celero -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} @@ -29,10 +31,18 @@ file(MAKE_DIRECTORY ${INSTALL_DIR}/include) add_library(gil_celero STATIC IMPORTED) target_include_directories(gil_celero INTERFACE ${INSTALL_DIR}/include) target_compile_definitions(gil_celero INTERFACE CELERO_STATIC=1) -set_property(TARGET gil_celero PROPERTY IMPORTED_LOCATION_DEBUG +set_property(TARGET gil_celero PROPERTY + IMPORTED_LOCATION_DEBUG "${INSTALL_DIR}/lib/static/${CMAKE_STATIC_LIBRARY_PREFIX}celerod${CMAKE_STATIC_LIBRARY_SUFFIX}") -set_property(TARGET gil_celero PROPERTY IMPORTED_LOCATION_RELEASE +set_property(TARGET gil_celero PROPERTY + IMPORTED_LOCATION_RELEASE "${INSTALL_DIR}/lib/static/${CMAKE_STATIC_LIBRARY_PREFIX}celero${CMAKE_STATIC_LIBRARY_SUFFIX}") +set_target_properties(gil_celero PROPERTIES + MAP_IMPORTED_CONFIG_MINSIZEREL Release + MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release) + +# Hack to help IDEs (e.g. VS) find headers when using simple generators like Ninja +include_directories(${INSTALL_DIR}/include) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") find_library(POWRPROF PowrProf.lib) From 877a6da193abacf66032b480ae6eeb722bd4a1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Fri, 21 Jun 2019 19:52:23 +0200 Subject: [PATCH 182/193] Add benchmark: histogram_gray8.cpp Benchmark of various pixel iteration and access methods for a contiguous images composed of homogeneous pixels with single 8-bit integer channel (grayscale). --- benchmark/celero/CMakeLists.txt | 3 - benchmark/celero/histogram_gray8.cpp | 249 ++++++++++++++++++ benchmark/celero/histogram_gray8_msvc1421.csv | 61 +++++ 3 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 benchmark/celero/histogram_gray8.cpp create mode 100644 benchmark/celero/histogram_gray8_msvc1421.csv diff --git a/benchmark/celero/CMakeLists.txt b/benchmark/celero/CMakeLists.txt index 03f822d7ed..93483c6c14 100644 --- a/benchmark/celero/CMakeLists.txt +++ b/benchmark/celero/CMakeLists.txt @@ -41,9 +41,6 @@ set_target_properties(gil_celero PROPERTIES MAP_IMPORTED_CONFIG_MINSIZEREL Release MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release) -# Hack to help IDEs (e.g. VS) find headers when using simple generators like Ninja -include_directories(${INSTALL_DIR}/include) - if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") find_library(POWRPROF PowrProf.lib) target_link_libraries(gil_celero INTERFACE POWRPROF) diff --git a/benchmark/celero/histogram_gray8.cpp b/benchmark/celero/histogram_gray8.cpp new file mode 100644 index 0000000000..6b58200f21 --- /dev/null +++ b/benchmark/celero/histogram_gray8.cpp @@ -0,0 +1,249 @@ +// +// Copyright 2019 Mateusz Loskot +// +// Distributed under 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 +// +// Benchmark of various pixel iteration and access methods for a contiguous images +// composed of homogeneous pixels with single 8-bit integer channel (grayscale). +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace gil = boost::gil; + +class RandomImageGray8Fixture : public celero::TestFixture +{ +public: + RandomImageGray8Fixture() : gen_(rd_()), uid_(0, 255), histogram_{0} + { + } + + std::vector getExperimentValues() const final + { + // Generate sequence of square image sizes + int const initial_power = static_cast(std::ceil(std::log(initial_image_size_) / std::log(2))); + + std::vector problem_space; + problem_space.resize(this->number_of_images_); + for (int i = 0; i < this->number_of_images_; i++) + { + problem_space[i] = { int64_t(pow(2, initial_power + i)) }; + } + return problem_space; + } + + void setUp(const celero::TestFixture::ExperimentValue& experimentValue) final + { + auto const size = static_cast(experimentValue.Value); + this->image_.recreate(size, size); + auto view = gil::view(this->image_); + for (auto it = view.begin(); it != view.end(); ++it) + *it = static_cast(uid_(gen_)); + } + + void clear_histogram() + { + this->histogram_.fill(0); + } + + void assert_histogram() + { +#ifndef NDEBUG + std::size_t const total = std::accumulate(this->histogram_.cbegin(), this->histogram_.cend(), 0U); + assert(total == gil::view(this->image_).size()); +#endif + } + + std::random_device rd_; + std::mt19937 gen_; + std::uniform_int_distribution uid_; + + std::array histogram_; + gil::gray8_image_t image_; +#ifdef NDEBUG + int const initial_image_size_{64}; + int const number_of_images_{6}; +#else + int const initial_image_size_{2}; + int const number_of_images_{1}; +#endif +}; + +#ifdef NDEBUG +constexpr int samples = 30; // Celero default minimum +constexpr int iterations = 100; +#else +constexpr int samples = 1; +constexpr int iterations = 1; +#endif + +BASELINE_F(HistogramGray8, 1d_raw_pointer, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::view(this->image_); + std::uint8_t* p = gil::interleaved_view_get_raw_data(view); + for (std::size_t i = 0, size = view.size(); i < size; i++) + ++h[p[i]]; + + this->assert_histogram(); +} + +BENCHMARK_F(HistogramGray8, 1d_index_operator, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::view(this->image_); + std::ptrdiff_t const pixel_count = view.width() * view.height(); + for (std::ptrdiff_t i = 0; i < pixel_count; i++) + ++h[view[i]]; + + this->assert_histogram(); +} + +BENCHMARK_F(HistogramGray8, 1d_index_at, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::view(this->image_); + std::ptrdiff_t const pixel_count = view.width() * view.height(); + for (std::ptrdiff_t i = 0; i < pixel_count; i++) + ++h[*view.at(i)]; + + this->assert_histogram(); +} + +BENCHMARK_F(HistogramGray8, 1d_iterator, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::const_view(this->image_); + for (auto it = view.begin(), end = view.end(); it != end; ++it) + ++h[*it]; + + this->assert_histogram(); +} + +BENCHMARK_F(HistogramGray8, 1d_reverse_iterator, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::const_view(this->image_); + for (auto it = view.rbegin(), end = view.rend(); it != end; ++it) + ++h[*it]; + + this->assert_histogram(); +} + + +BENCHMARK_F(HistogramGray8, 1d_for_each_pixel, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::const_view(this->image_); + assert(view.is_1d_traversable()); + + gil::for_each_pixel(view, [&h](gil::gray8c_pixel_t const& pixel) { + ++h[pixel]; + }); + + this->assert_histogram(); +} + +BENCHMARK_F(HistogramGray8, 2d_call_operator, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::const_view(this->image_); + auto const view_h = view.height(); + auto const view_w = view.width(); + for (std::ptrdiff_t y = 0; y < view_h; y++) + { + for (std::ptrdiff_t x = 0; x < view_w; x++) + { + // The binary call operator() computes the location of the pixel in a 2D grid, + // which involves addition and multiplication, thus it costs performance. + ++h[view(x, y)]; + } + } + + this->assert_histogram(); +} + +BENCHMARK_F(HistogramGray8, 2d_xy_locator, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::const_view(this->image_); + auto loc = view.xy_at(0, 0); + auto const view_h = view.height(); + auto const view_w = view.width(); + for (std::ptrdiff_t y = 0; y < view_h; y++) + { + for (std::ptrdiff_t x = 0; x < view_w; x++) + { + ++h[loc(0, 0)]; + ++loc.x(); + } + // carriage return, equivalent of: loc.x() -= view.width(); ++loc.y(); + loc += gil::point_t{-view.width(), 1}; + } + + this->assert_histogram(); +} + +BENCHMARK_F(HistogramGray8, 2d_x_iterator, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::const_view(this->image_); + auto const view_h = view.height(); + for (std::ptrdiff_t y = 0; y < view_h; y++) + { + // The two iterator types are raw C pointers, so the dereferencing + // and arithmetic is a fast pointer indexing operator. + gil::gray8c_view_t::x_iterator it = view.row_begin(y); + gil::gray8c_view_t::x_iterator end = view.row_end(y); + while (it != end) + ++h[*it++]; + } + + this->assert_histogram(); +} + +BENCHMARK_F(HistogramGray8, 2d_y_iterator, RandomImageGray8Fixture, samples, iterations) +{ + this->clear_histogram(); + auto& h = this->histogram_; + + auto view = gil::const_view(this->image_); + auto const view_w = view.width(); + for (std::ptrdiff_t x = 0; x < view_w; x++) + { + gil::gray8c_view_t::y_iterator it = view.col_begin(x); + gil::gray8c_view_t::y_iterator end = view.col_end(x); + while (it != end) + ++h[*it++]; + } + + this->assert_histogram(); +} + +CELERO_MAIN diff --git a/benchmark/celero/histogram_gray8_msvc1421.csv b/benchmark/celero/histogram_gray8_msvc1421.csv new file mode 100644 index 0000000000..56bfa7acfa --- /dev/null +++ b/benchmark/celero/histogram_gray8_msvc1421.csv @@ -0,0 +1,61 @@ +Group,Experiment,Problem Space,Samples,Iterations,Failure,Baseline,us/Iteration,Iterations/sec,Min (us),Mean (us),Max (us),Variance,Standard Deviation,Skewness,Kurtosis,Z Score +HistogramGray8,1d_raw_pointer,64,30,100,0,1,2.32,431034,232,280.467,322,697.016,26.4011,-0.432173,-0.936804,1.83578 +HistogramGray8,1d_raw_pointer,128,30,100,0,1,9.79,102145,979,1126.37,1240,4270.79,65.3513,-0.355228,-0.486781,2.25499 +HistogramGray8,1d_raw_pointer,256,30,100,0,1,39.39,25387.2,3939,4276.27,4680,35999.6,189.736,0.383771,-0.483897,1.77756 +HistogramGray8,1d_raw_pointer,512,30,100,0,1,164.65,6073.49,16465,16804.3,17484,61781,248.558,1.14495,0.919333,1.36507 +HistogramGray8,1d_raw_pointer,1024,30,100,0,1,671.87,1488.38,67187,69849.2,73740,3.51459e+06,1874.72,0.810687,-0.450895,1.42005 +HistogramGray8,1d_raw_pointer,2048,30,100,0,1,2664.44,375.313,266444,276317,295826,8.60814e+07,9278,0.776554,-0.897073,1.06416 +HistogramGray8,1d_index_operator,64,30,100,0,18.7198,43.43,23025.6,4343,4688.37,5251,51378.2,226.668,0.676256,-0.011044,1.52367 +HistogramGray8,1d_index_operator,128,30,100,0,18.6139,182.23,5487.57,18223,18628.8,20005,164651,405.772,2.18133,4.14667,1.00015 +HistogramGray8,1d_index_operator,256,30,100,0,18.5359,730.13,1369.62,73013,74479.9,78574,1.40907e+06,1187.04,1.99851,4.03504,1.23573 +HistogramGray8,1d_index_operator,512,30,100,0,17.8932,2946.12,339.429,294612,298057,304145,7.9811e+06,2825.08,0.641686,-0.823317,1.21959 +HistogramGray8,1d_index_operator,1024,30,100,0,17.6477,11857,84.3385,1185698,1.19325e+06,1206125,3.6547e+07,6045.41,0.829102,-0.460791,1.24976 +HistogramGray8,1d_index_operator,2048,30,100,0,17.7861,47389.9,21.1015,4738990,4.78771e+06,4879534,8.66826e+08,29441.9,1.11416,1.74636,1.65492 +HistogramGray8,1d_index_at,64,30,100,0,17.1293,39.74,25163.6,3974,4430.83,4855,42511.5,206.183,-0.313806,-0.464758,2.21567 +HistogramGray8,1d_index_at,128,30,100,0,16.8069,164.54,6077.55,16454,16824.5,17412,44019.2,209.808,0.715802,0.423865,1.76574 +HistogramGray8,1d_index_at,256,30,100,0,16.834,663.09,1508.09,66309,68026.6,73972,3.43666e+06,1853.82,1.92032,2.72669,0.9265 +HistogramGray8,1d_index_at,512,30,100,0,16.2039,2667.98,374.815,266798,274866,293504,4.09254e+07,6397.29,1.05313,0.685735,1.26118 +HistogramGray8,1d_index_at,1024,30,100,0,16.1346,10840.4,92.2479,1084036,1.09391e+06,1121849,9.23607e+07,9610.45,1.2973,0.911113,1.02707 +HistogramGray8,1d_index_at,2048,30,100,0,15.9902,42604.9,23.4715,4260491,4.34556e+06,4442077,1.67326e+09,40905.5,0.425214,0.56668,2.07969 +HistogramGray8,1d_iterator,64,30,100,0,1.73707,4.03,248139,403,427.667,501,1011.54,31.8047,1.05332,-0.410291,0.775566 +HistogramGray8,1d_iterator,128,30,100,0,1.64045,16.06,62266.5,1606,1804.7,2071,19371.5,139.182,0.408106,-1.09814,1.42763 +HistogramGray8,1d_iterator,256,30,100,0,1.68291,66.29,15085.2,6629,7421.97,8110,114491,338.364,-0.0201391,-0.213858,2.34353 +HistogramGray8,1d_iterator,512,30,100,0,1.62903,268.22,3728.28,26822,27803,28930,453091,673.12,0.3422,-1.2403,1.45744 +HistogramGray8,1d_iterator,1024,30,100,0,1.60844,1080.66,925.36,108066,113580,120037,1.17025e+07,3420.89,0.12696,-1.01499,1.61199 +HistogramGray8,1d_iterator,2048,30,100,0,1.63676,4361.04,229.303,436104,443403,454410,2.48219e+07,4982.16,0.363708,-0.518366,1.46495 +HistogramGray8,1d_reverse_iterator,64,30,100,0,10.2026,23.67,42247.6,2367,2522.4,2785,11358.9,106.578,0.470624,-0.539924,1.45808 +HistogramGray8,1d_reverse_iterator,128,30,100,0,9.57099,93.7,10672.4,9370,9735.77,10469,57530.1,239.854,0.80815,0.991489,1.52495 +HistogramGray8,1d_reverse_iterator,256,30,100,0,9.66844,380.84,2625.77,38084,38736.8,41276,497834,705.573,2.27396,5.24665,0.925252 +HistogramGray8,1d_reverse_iterator,512,30,100,0,9.21427,1517.13,659.139,151713,154460,159563,4.09464e+06,2023.52,1.07982,0.213067,1.35752 +HistogramGray8,1d_reverse_iterator,1024,30,100,0,9.08338,6102.85,163.858,610285,619071,651357,6.87241e+07,8290,2.32861,6.05929,1.05984 +HistogramGray8,1d_reverse_iterator,2048,30,100,0,9.20704,24531.6,40.7637,2453160,2.48654e+06,2581765,9.35506e+08,30586,1.32276,1.45197,1.09129 +HistogramGray8,1d_for_each_pixel,64,30,100,0,1.00431,2.33,429185,233,248.333,313,384.437,19.6071,1.52322,2.03799,0.782031 +HistogramGray8,1d_for_each_pixel,128,30,100,0,0.947906,9.28,107759,928,993.467,1107,2591.57,50.9074,0.862133,-0.0363922,1.28599 +HistogramGray8,1d_for_each_pixel,256,30,100,0,0.980452,38.62,25893.3,3862,4066.2,4603,29286.2,171.132,1.36057,1.57314,1.19323 +HistogramGray8,1d_for_each_pixel,512,30,100,0,0.960887,158.21,6320.71,15821,16276.6,16725,45932.7,214.319,0.152992,-0.527451,2.12596 +HistogramGray8,1d_for_each_pixel,1024,30,100,0,0.959977,644.98,1550.44,64498,67003.9,69976,3.28565e+06,1812.64,0.192411,-1.44393,1.38248 +HistogramGray8,1d_for_each_pixel,2048,30,100,0,0.973158,2592.92,385.666,259292,264311,284740,3.37305e+07,5807.8,1.93337,3.57344,0.864102 +HistogramGray8,2d_call_operator,64,30,100,0,1.15948,2.69,371747,269,284.5,328,284.397,16.8641,0.867782,-0.398824,0.919114 +HistogramGray8,2d_call_operator,128,30,100,0,1.02962,10.08,99206.3,1008,1086.73,1373,5873.03,76.6357,2.1662,5.30694,1.02737 +HistogramGray8,2d_call_operator,256,30,100,0,1.02209,40.26,24838.5,4026,4232.7,4772,34821.9,186.606,1.38605,1.60431,1.10768 +HistogramGray8,2d_call_operator,512,30,100,0,0.991862,163.31,6123.32,16331,16726.8,17962,120025,346.446,1.62892,3.44857,1.14255 +HistogramGray8,2d_call_operator,1024,30,100,0,0.973983,654.39,1528.14,65439,67781.8,74904,6.39585e+06,2529,1.86477,2.31278,0.926387 +HistogramGray8,2d_call_operator,2048,30,100,0,0.987498,2631.13,380.065,263113,269756,283907,1.77622e+07,4214.52,1.08199,2.37988,1.57616 +HistogramGray8,2d_xy_locator,64,30,100,0,1.28017,2.97,336700,297,335.3,418,1101.8,33.1934,0.657576,-0.443954,1.15384 +HistogramGray8,2d_xy_locator,128,30,100,0,1.09602,10.73,93196.6,1073,1167.1,1400,8416.23,91.74,0.996443,-0.0239161,1.02572 +HistogramGray8,2d_xy_locator,256,30,100,0,1.06017,41.76,23946.4,4176,4497.53,5272,64497.6,253.964,1.19077,1.07985,1.26606 +HistogramGray8,2d_xy_locator,512,30,100,0,1.01688,167.43,5972.65,16743,17217.8,18244,101075,317.923,1.52556,2.42308,1.49355 +HistogramGray8,2d_xy_locator,1024,30,100,0,1.00134,672.77,1486.39,67277,68350.1,72583,1.43459e+06,1197.74,2.0853,4.12646,0.895908 +HistogramGray8,2d_xy_locator,2048,30,100,0,1.00278,2671.85,374.273,267185,273451,290045,2.51464e+07,5014.62,1.52825,2.64603,1.24964 +HistogramGray8,2d_x_iterator,64,30,100,0,1.22845,2.85,350877,285,305.667,381,657.264,25.6372,1.18695,0.627509,0.806121 +HistogramGray8,2d_x_iterator,128,30,100,0,1.04903,10.27,97371,1027,1106.03,1312,4123.48,64.2143,1.27747,1.78535,1.23077 +HistogramGray8,2d_x_iterator,256,30,100,0,0.997715,39.3,25445.3,3930,4137.2,4527,22703.5,150.677,0.807551,0.0156739,1.37513 +HistogramGray8,2d_x_iterator,512,30,100,0,0.966535,159.14,6283.78,15914,16372.9,17810,113398,336.746,2.63579,9.20267,1.36265 +HistogramGray8,2d_x_iterator,1024,30,100,0,0.937369,629.79,1587.83,62979,64549.3,67035,1.06198e+06,1030.52,1.19112,0.780298,1.52382 +HistogramGray8,2d_x_iterator,2048,30,100,0,0.958025,2552.6,391.757,255260,258531,269247,8.45361e+06,2907.51,1.93487,4.44336,1.12499 +HistogramGray8,2d_y_iterator,64,30,100,0,9.35345,21.7,46082.9,2170,2328.73,2594,9612.89,98.0454,0.502065,0.0544133,1.61898 +HistogramGray8,2d_y_iterator,128,30,100,0,9.03984,88.5,11299.4,8850,9267.2,9993,109674,331.171,0.830047,-0.446344,1.25977 +HistogramGray8,2d_y_iterator,256,30,100,0,10.1318,399.09,2505.7,39909,40807.6,43780,466008,682.648,2.80346,10.1115,1.31635 +HistogramGray8,2d_y_iterator,512,30,100,0,10.8486,1786.22,559.841,178622,180727,185148,2.59968e+06,1612.35,0.994626,0.512186,1.30553 +HistogramGray8,2d_y_iterator,1024,30,100,0,18.3811,12349.7,80.9734,1234974,1.25622e+06,1290899,2.7257e+08,16509.7,0.509655,-0.913002,1.28703 +HistogramGray8,2d_y_iterator,2048,30,100,0,21.9414,58461.5,17.1053,5846148,6.05445e+06,6154576,3.74868e+09,61226.5,-1.51749,3.13101,3.40209 From 4136e2f7294b7422fa07058ba1a8b12f7f34d55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Sat, 22 Jun 2019 23:51:30 +0200 Subject: [PATCH 183/193] Add header this is benchmark prototype to README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b46d70a57d..d47a5048d1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# WIP: Benchmark Prototype + ![Boost Generic Image Library (GIL)](https://raw.githubusercontent.com/boostorg/gil/develop/doc/_static/gil.png) [![Language](https://img.shields.io/badge/C%2B%2B-14-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) From f292202460a51826711b31d09230150f87b0bf06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81oskot?= Date: Mon, 24 Jun 2019 22:31:39 +0200 Subject: [PATCH 184/193] Benchmark: Add charts for Celero benchmark results --- ...stogram_gray8_msvc1421_HistogramGray8.html | 85 +++++++++++++++++++ benchmark/celero/charts/index.html | 6 ++ 2 files changed, 91 insertions(+) create mode 100644 benchmark/celero/charts/histogram_gray8_msvc1421_HistogramGray8.html create mode 100644 benchmark/celero/charts/index.html diff --git a/benchmark/celero/charts/histogram_gray8_msvc1421_HistogramGray8.html b/benchmark/celero/charts/histogram_gray8_msvc1421_HistogramGray8.html new file mode 100644 index 0000000000..dcf9f6aeac --- /dev/null +++ b/benchmark/celero/charts/histogram_gray8_msvc1421_HistogramGray8.html @@ -0,0 +1,85 @@ + + + + + + + + + + + Benchmark results for 'HistogramGray8' + + + + + + + + + + + + + + + + + + + + + + + + +

            + + + + + + + + + + + \ No newline at end of file diff --git a/benchmark/celero/charts/index.html b/benchmark/celero/charts/index.html new file mode 100644 index 0000000000..0428ca1fd2 --- /dev/null +++ b/benchmark/celero/charts/index.html @@ -0,0 +1,6 @@ + + Celero Benchmarks + +

            Celero Benchmarks

            +

            List of benchmark groups:

            +
            Generated by pycelerograph | Benchmark by Celero \ No newline at end of file From a02e9f1e0b3c27c2d32bc3ea6aafad93aaefdb7f Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 14 Oct 2020 14:06:22 +0200 Subject: [PATCH 185/193] Add benchmarks for rotation and flip with OpenCV implementation --- bench/CMakeLists.txt | 26 ++++++++++++++ bench/bench_flip.cpp | 45 ++++++++++++++++++++++++ bench/bench_rotation.cpp | 62 +++++++++++++++++++++++++++++++++ bench/opencv/CMakeLists.txt | 31 +++++++++++++++++ bench/opencv/bench_flip.cpp | 43 +++++++++++++++++++++++ bench/opencv/bench_rotation.cpp | 59 +++++++++++++++++++++++++++++++ 6 files changed, 266 insertions(+) create mode 100644 bench/CMakeLists.txt create mode 100644 bench/bench_flip.cpp create mode 100644 bench/bench_rotation.cpp create mode 100644 bench/opencv/CMakeLists.txt create mode 100644 bench/opencv/bench_flip.cpp create mode 100644 bench/opencv/bench_rotation.cpp diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt new file mode 100644 index 0000000000..5e9cb4f26b --- /dev/null +++ b/bench/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2020 Samuel Debionne, ESRF. + +# Use, modification and distribution is 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) + +add_executable(bench_rotation + bench_rotation.cpp +) + +target_link_libraries(bench_rotation + benchmark::benchmark +) + +add_executable(bench_flip + bench_flip.cpp +) + +target_link_libraries(bench_flip + benchmark::benchmark +) + +if(BUILD_BENCHMARK_OPENCV) + find_package(OpenCV REQUIRED) + add_subdirectory(bench) +endif() diff --git a/bench/bench_flip.cpp b/bench/bench_flip.cpp new file mode 100644 index 0000000000..1d61d09a72 --- /dev/null +++ b/bench/bench_flip.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void flip_up_down(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + auto flipped = flipped_up_down_view(const_view(in)); + copy_pixels(flipped, view(out)); + } +} +BENCHMARK(flip_up_down)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void flip_left_right(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + auto flipped = flipped_left_right_view(const_view(in)); + copy_pixels(flipped, view(out)); + } +} +BENCHMARK(flip_left_right)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); diff --git a/bench/bench_rotation.cpp b/bench/bench_rotation.cpp new file mode 100644 index 0000000000..eea32e52c6 --- /dev/null +++ b/bench/bench_rotation.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void rotation_cc90(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + auto rotated = rotated90cw_view(const_view(in)); + copy_pixels(rotated, view(out)); + } +} +BENCHMARK(rotation_cc90)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void rotation_ccw90(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + auto rotated = rotated90ccw_view(const_view(in)); + copy_pixels(rotated, view(out)); + } +} +BENCHMARK(rotation_ccw90)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void rotation_180(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + auto rotated = rotated180_view(const_view(in)); + copy_pixels(rotated, view(out)); + } +} +BENCHMARK(rotation_180)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); diff --git a/bench/opencv/CMakeLists.txt b/bench/opencv/CMakeLists.txt new file mode 100644 index 0000000000..1009be75ab --- /dev/null +++ b/bench/opencv/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (C) 2020 Samuel Debionne, ESRF. + +# Use, modification and distribution is 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) + +add_executable(bench_rotation + bench_rotation.cpp +) + +target_link_libraries(bench_rotation + benchmark::benchmark + ${OpenCV_LIBS} +) + +target_include_directories(bench_rotation + ${OpenCV_INCLUDE_DIRS} +) + +add_executable(bench_flip + bench_flip.cpp +) + +target_link_libraries(bench_flip + benchmark::benchmark + ${OpenCV_LIBS} +) + +target_include_directories(bench_flip + ${OpenCV_INCLUDE_DIRS} +) diff --git a/bench/opencv/bench_flip.cpp b/bench/opencv/bench_flip.cpp new file mode 100644 index 0000000000..51d3d9e34a --- /dev/null +++ b/bench/opencv/bench_flip.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void flip_up_down(benchmark::State& state) +{ + using namespace cv; + + int dim = state.range(0); + + Mat1b in(dim, dim); + Mat1b out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + flip(in,out,0); + } +} +BENCHMARK(flip_up_down)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void flip_left_right(benchmark::State& state) +{ + using namespace cv; + + int dim = state.range(0); + + Mat1b in(dim, dim); + Mat1b out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + flip(in,out,1); + } +} +BENCHMARK(flip_left_right)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); diff --git a/bench/opencv/bench_rotation.cpp b/bench/opencv/bench_rotation.cpp new file mode 100644 index 0000000000..3e5270c13b --- /dev/null +++ b/bench/opencv/bench_rotation.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void rotation_cc90(benchmark::State& state) +{ + using namespace cv; + + size_t dim = state.range(0); + + Mat1b in(dim, dim); + Mat1b out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + rotate(in,out,ROTATE_90_CLOCKWISE); + } +} +BENCHMARK(rotation_cc90)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void rotation_ccw90(benchmark::State& state) +{ + using namespace cv; + + size_t dim = state.range(0); + + Mat1b in(dim, dim); + Mat1b out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + rotate(in,out,ROTATE_90_COUNTERCLOCKWISE); + } +} +BENCHMARK(rotation_ccw90)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void rotation_180(benchmark::State& state) +{ + using namespace cv; + + size_t dim = state.range(0); + + Mat1b in(dim, dim); + Mat1b out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + rotate(in,out,ROTATE_180); + } +} +BENCHMARK(rotation_180)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); From 0b8410818bcc9f0b6e7e4fd29d47e44af0876944 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 14 Oct 2020 14:42:54 +0200 Subject: [PATCH 186/193] fixup! Add benchmarks for rotation and flip with OpenCV implementation --- bench/CMakeLists.txt | 2 +- bench/opencv/CMakeLists.txt | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 5e9cb4f26b..94a81da3b7 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -22,5 +22,5 @@ target_link_libraries(bench_flip if(BUILD_BENCHMARK_OPENCV) find_package(OpenCV REQUIRED) - add_subdirectory(bench) + add_subdirectory(opencv) endif() diff --git a/bench/opencv/CMakeLists.txt b/bench/opencv/CMakeLists.txt index 1009be75ab..13879a58cd 100644 --- a/bench/opencv/CMakeLists.txt +++ b/bench/opencv/CMakeLists.txt @@ -4,28 +4,30 @@ # License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) -add_executable(bench_rotation +add_executable(bench_ocv_rotation bench_rotation.cpp ) -target_link_libraries(bench_rotation +target_link_libraries(bench_ocv_rotation benchmark::benchmark ${OpenCV_LIBS} ) -target_include_directories(bench_rotation - ${OpenCV_INCLUDE_DIRS} +message(${OpenCV_INCLUDE_DIRS}) + +target_include_directories(bench_ocv_rotation + PRIVATE ${OpenCV_INCLUDE_DIRS} ) -add_executable(bench_flip +add_executable(bench_ocv_flip bench_flip.cpp ) -target_link_libraries(bench_flip +target_link_libraries(bench_ocv_flip benchmark::benchmark ${OpenCV_LIBS} ) -target_include_directories(bench_flip - ${OpenCV_INCLUDE_DIRS} +target_include_directories(bench_ocv_flip + PRIVATE ${OpenCV_INCLUDE_DIRS} ) From 51630ea221d0eaaa766b065542a3ff0d9c3dc9f5 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 15 Oct 2020 11:35:57 +0200 Subject: [PATCH 187/193] Add blaze binding benchmark for transpose --- bench/CMakeLists.txt | 15 +++++++- bench/bench_rotation.cpp | 38 +++++++++++++++++++ bench/bench_transpose.cpp | 28 ++++++++++++++ bench/blaze/CMakeLists.txt | 27 ++++++++++++++ bench/blaze/bench_transpose.cpp | 63 ++++++++++++++++++++++++++++++++ bench/opencv/CMakeLists.txt | 15 +++++++- bench/opencv/bench_transpose.cpp | 42 +++++++++++++++++++++ 7 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 bench/bench_transpose.cpp create mode 100644 bench/blaze/CMakeLists.txt create mode 100644 bench/blaze/bench_transpose.cpp create mode 100644 bench/opencv/bench_transpose.cpp diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 94a81da3b7..28d46bc03f 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -5,7 +5,7 @@ # http://www.boost.org/LICENSE_1_0.txt) add_executable(bench_rotation - bench_rotation.cpp + bench_rotation.cpp ) target_link_libraries(bench_rotation @@ -20,7 +20,20 @@ target_link_libraries(bench_flip benchmark::benchmark ) +add_executable(bench_transpose + bench_transpose.cpp +) + +target_link_libraries(bench_transpose + benchmark::benchmark +) + if(BUILD_BENCHMARK_OPENCV) find_package(OpenCV REQUIRED) add_subdirectory(opencv) endif() + +if(BUILD_BENCHMARK_BLAZE) + find_package(blaze REQUIRED) + add_subdirectory(blaze) +endif() \ No newline at end of file diff --git a/bench/bench_rotation.cpp b/bench/bench_rotation.cpp index eea32e52c6..497b46e2de 100644 --- a/bench/bench_rotation.cpp +++ b/bench/bench_rotation.cpp @@ -25,6 +25,25 @@ static void rotation_cc90(benchmark::State& state) } BENCHMARK(rotation_cc90)->RangeMultiplier(2)->Range(256, 8 << 10); +static void rotation_cc90_v2(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + auto transposed = transposed_view(const_view(in)); + copy_pixels(transposed, view(out)); + auto flipped = flipped_left_right_view(const_view(out)); + copy_pixels(flipped, view(out)); + } +} +BENCHMARK(rotation_cc90_v2)->RangeMultiplier(2)->Range(256, 8 << 10); + static void rotation_ccw90(benchmark::State& state) { using namespace boost::gil; @@ -42,6 +61,25 @@ static void rotation_ccw90(benchmark::State& state) } BENCHMARK(rotation_ccw90)->RangeMultiplier(2)->Range(256, 8 << 10); +static void rotation_ccw90_v2(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + auto transposed = transposed_view(const_view(in)); + copy_pixels(transposed, view(out)); + auto flipped = flipped_up_down_view(const_view(out)); + copy_pixels(flipped, view(out)); + } +} +BENCHMARK(rotation_ccw90_v2)->RangeMultiplier(2)->Range(256, 8 << 10); + static void rotation_180(benchmark::State& state) { using namespace boost::gil; diff --git a/bench/bench_transpose.cpp b/bench/bench_transpose.cpp new file mode 100644 index 0000000000..887672788f --- /dev/null +++ b/bench/bench_transpose.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void transpose(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + auto transposed = transposed_view(const_view(in)); + copy_pixels(transposed, view(out)); + } +} +BENCHMARK(transpose)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); diff --git a/bench/blaze/CMakeLists.txt b/bench/blaze/CMakeLists.txt new file mode 100644 index 0000000000..e55e70a885 --- /dev/null +++ b/bench/blaze/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright (C) 2020 Samuel Debionne, ESRF. + +# Use, modification and distribution is 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) + +add_executable(bench_blaze_transpose + bench_transpose.cpp +) + +target_link_libraries(bench_blaze_transpose + benchmark::benchmark + blaze::blaze +) + +target_compile_definitions(bench_blaze_transpose + PRIVATE BLAZE_USE_SHARED_MEMORY_PARALLELIZATION=0 +) + +add_executable(bench_blaze_transpose_mt + bench_transpose.cpp +) + +target_link_libraries(bench_blaze_transpose_mt + benchmark::benchmark + blaze::blaze +) diff --git a/bench/blaze/bench_transpose.cpp b/bench/blaze/bench_transpose.cpp new file mode 100644 index 0000000000..dfeaf425b3 --- /dev/null +++ b/bench/blaze/bench_transpose.cpp @@ -0,0 +1,63 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +template +auto as_matrix(GrayView const& source) +{ + using channel_t = typename boost::gil::channel_type::type; + //propagate_const on channel_t + + return blaze::CustomMatrix( + reinterpret_cast(&source(0, 0)), + source.height(), + source.width()); +} + +static void blaze_transpose(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + auto mat_in = as_matrix(view(in)); + auto mat_out = as_matrix(view(in)); + + for (auto _ : state) { + // The code to benchmark + mat_out = blaze::trans(mat_in); + } +} +BENCHMARK(blaze_transpose)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void blaze_transpose_inplace(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + + auto mat_in_out = as_matrix(view(in)); + + for (auto _ : state) { + // The code to benchmark + blaze::transpose(mat_in_out); + } +} +BENCHMARK(blaze_transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); diff --git a/bench/opencv/CMakeLists.txt b/bench/opencv/CMakeLists.txt index 13879a58cd..0aabb76851 100644 --- a/bench/opencv/CMakeLists.txt +++ b/bench/opencv/CMakeLists.txt @@ -5,7 +5,7 @@ # http://www.boost.org/LICENSE_1_0.txt) add_executable(bench_ocv_rotation - bench_rotation.cpp + bench_rotation.cpp ) target_link_libraries(bench_ocv_rotation @@ -31,3 +31,16 @@ target_link_libraries(bench_ocv_flip target_include_directories(bench_ocv_flip PRIVATE ${OpenCV_INCLUDE_DIRS} ) + +add_executable(bench_ocv_transpose + bench_transpose.cpp +) + +target_link_libraries(bench_ocv_transpose + benchmark::benchmark + ${OpenCV_LIBS} +) + +target_include_directories(bench_ocv_transpose + PRIVATE ${OpenCV_INCLUDE_DIRS} +) diff --git a/bench/opencv/bench_transpose.cpp b/bench/opencv/bench_transpose.cpp new file mode 100644 index 0000000000..26bf552a65 --- /dev/null +++ b/bench/opencv/bench_transpose.cpp @@ -0,0 +1,42 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void transpose(benchmark::State& state) +{ + using namespace cv; + + size_t dim = state.range(0); + + Mat1b in(dim, dim); + Mat1b out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + transpose(in,out); + } +} +BENCHMARK(transpose)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void transpose_inplace(benchmark::State& state) +{ + using namespace cv; + + size_t dim = state.range(0); + + Mat1b in(dim, dim); + + for (auto _ : state) { + // The code to benchmark + transpose(in,in); + } +} +BENCHMARK(transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); From b671adcd8812255f4365012054d080938c77cda8 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 15 Oct 2020 17:10:32 +0200 Subject: [PATCH 188/193] Add transpose implementation that uses blocking --- bench/CMakeLists.txt | 8 +++ bench/bench_transpose_impl.cpp | 93 +++++++++++++++++++++++++++++++++ bench/blaze/bench_transpose.cpp | 3 +- 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 bench/bench_transpose_impl.cpp diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 28d46bc03f..15d202c85b 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -28,6 +28,14 @@ target_link_libraries(bench_transpose benchmark::benchmark ) +add_executable(bench_transpose_impl + bench_transpose_impl.cpp +) + +target_link_libraries(bench_transpose_impl + benchmark::benchmark +) + if(BUILD_BENCHMARK_OPENCV) find_package(OpenCV REQUIRED) add_subdirectory(opencv) diff --git a/bench/bench_transpose_impl.cpp b/bench/bench_transpose_impl.cpp new file mode 100644 index 0000000000..ca82db09a4 --- /dev/null +++ b/bench/bench_transpose_impl.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +#define GIL_ENABLE_UNROLLED 1 + +namespace boost { + namespace gil { + + template + static void transpose(const SrcView& src, const DstView& dst) + { + int i, j; + auto m = src.width(); + auto n = src.height(); + + static const int block_size = 4; + + std::array l; + std::generate(l.begin(), l.end(), [loc = src.xy_at(0, 0), i = 0]() mutable { + auto res = loc.cache_location(i % block_size, i / block_size); + i++; + return res; + }); + +#if GIL_ENABLE_UNROLLED + for (i = 0; i <= m - block_size; i += block_size) + { + auto d0 = dst.row_begin(i); + auto d1 = dst.row_begin(i + 1); + auto d2 = dst.row_begin(i + 2); + auto d3 = dst.row_begin(i + 3); + + for (j = 0; j <= n - block_size; j += block_size) + { + auto s = src.xy_at(i, j); + + d0[j] = s[l[0]]; d0[j + 1] = s[l[4]]; d0[j + 2] = s[l[8]]; d0[j + 3] = s[l[12]]; + d1[j] = s[l[1]]; d1[j + 1] = s[l[5]]; d1[j + 2] = s[l[9]]; d1[j + 3] = s[l[13]]; + d2[j] = s[l[2]]; d2[j + 1] = s[l[6]]; d2[j + 2] = s[l[10]]; d2[j + 3] = s[l[14]]; + d3[j] = s[l[3]]; d3[j + 1] = s[l[7]]; d3[j + 2] = s[l[11]]; d3[j + 3] = s[l[15]]; + } + + for (; j < n; j++) + { + auto s = src.xy_at(i, j); + + d0[j] = s[l[0]]; d1[j] = s[l[1]]; d2[j] = s[l[2]]; d3[j] = s[l[3]]; + } + } +#endif + for (; i < m; i++) + { + auto d0 = dst.row_begin(i); +#if GIL_ENABLE_UNROLLED + for (j = 0; j <= n - block_size; j += block_size) + { + auto s = src.xy_at(i, j); + + d0[j] = s[l[0]]; d0[j + 1] = s[l[4]]; d0[j + 2] = s[l[8]]; d0[j + 3] = s[l[12]]; + } +#endif + for (; j < n; j++) + d0[j] = src(i, j); + } + } + + } +} //namespace boost::gil + +static void transpose(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + for (auto _ : state) { + // The code to benchmark + transpose(const_view(in), view(out)); + } +} +BENCHMARK(transpose)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/bench/blaze/bench_transpose.cpp b/bench/blaze/bench_transpose.cpp index dfeaf425b3..a076877b25 100644 --- a/bench/blaze/bench_transpose.cpp +++ b/bench/blaze/bench_transpose.cpp @@ -16,10 +16,9 @@ template ::type; - //propagate_const on channel_t return blaze::CustomMatrix( - reinterpret_cast(&source(0, 0)), + boost::gil::interleaved_view_get_raw_data(source), source.height(), source.width()); } From b94bf232591ade25ba8023ab108071ae33d286f9 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Fri, 16 Oct 2020 13:24:52 +0200 Subject: [PATCH 189/193] Add IPP benchmark --- bench/CMakeLists.txt | 15 +++++++++ bench/ipp/CMakeLists.txt | 23 ++++++++++++++ bench/ipp/bench_transpose.cpp | 53 ++++++++++++++++++++++++++++++++ bench/ipp/test_transpose.cpp | 58 +++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 bench/ipp/CMakeLists.txt create mode 100644 bench/ipp/bench_transpose.cpp create mode 100644 bench/ipp/test_transpose.cpp diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 15d202c85b..033ca5826a 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -44,4 +44,19 @@ endif() if(BUILD_BENCHMARK_BLAZE) find_package(blaze REQUIRED) add_subdirectory(blaze) +endif() + +if(BUILD_BENCHMARK_IPP) + #find_package(IPP REQUIRED) + find_path(IPP_INCLUDE_DIR ipp.h PATHS ${IPP_ROOT}/include) + message("IPP_INCLUDE_DIR: ${IPP_INCLUDE_DIR}") + find_library(IPP_LIB_CORE ippcore PATHS ${IPP_ROOT}/lib) + find_library(IPP_LIB_I ippi PATHS ${IPP_ROOT}/lib) + message("IPP_LIB_CORE: ${IPP_LIB_CORE}") + message("IPP_LIB_I: ${IPP_LIB_I}") + add_library(ipp::ipp INTERFACE IMPORTED) + target_include_directories(ipp::ipp INTERFACE ${IPP_INCLUDE_DIR}) + target_link_libraries(ipp::ipp INTERFACE ${IPP_LIB_CORE} ${IPP_LIB_I}) + + add_subdirectory(ipp) endif() \ No newline at end of file diff --git a/bench/ipp/CMakeLists.txt b/bench/ipp/CMakeLists.txt new file mode 100644 index 0000000000..7eb26f0ede --- /dev/null +++ b/bench/ipp/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (C) 2020 Samuel Debionne, ESRF. + +# Use, modification and distribution is 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) + +add_executable(bench_ipp_transpose + bench_transpose.cpp +) + +target_link_libraries(bench_ipp_transpose + benchmark::benchmark + ipp::ipp +) + + +add_executable(test_ipp_transpose + test_transpose.cpp +) + +target_link_libraries(test_ipp_transpose + ipp::ipp +) \ No newline at end of file diff --git a/bench/ipp/bench_transpose.cpp b/bench/ipp/bench_transpose.cpp new file mode 100644 index 0000000000..8396f4dd10 --- /dev/null +++ b/bench/ipp/bench_transpose.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void ipp_transpose(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + IppiSize srcRoi = { dim, dim }; + + for (auto _ : state) { + // The code to benchmark + ippiTranspose_8u_C1R( + boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) in.width(), + boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), + srcRoi); + } +} +BENCHMARK(ipp_transpose)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void ipp_transpose_inplace(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + + IppiSize srcRoi = { dim, dim }; + + for (auto _ : state) { + // The code to benchmark + ippiTranspose_8u_C1IR( + boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), + srcRoi); + } +} +BENCHMARK(ipp_transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); diff --git a/bench/ipp/test_transpose.cpp b/bench/ipp/test_transpose.cpp new file mode 100644 index 0000000000..467f05c102 --- /dev/null +++ b/bench/ipp/test_transpose.cpp @@ -0,0 +1,58 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void test_transpose() +{ + using namespace boost::gil; + + size_t dim = 1024; + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); + + IppiSize srcRoi = { dim, dim }; + ippiTranspose_8u_C1R( + boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) in.width(), + boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), + srcRoi); + + BOOST_TEST(equal_pixels(transposed_view(const_view(in)), const_view(out))); +} + +static void test_transpose_inplace() +{ + using namespace boost::gil; + + size_t dim = 1024; + + gray8_image_t in(dim, dim); + + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); + + gray8_image_t in_ref(in); + + IppiSize srcRoi = { dim, dim }; + ippiTranspose_8u_C1IR( + boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), + srcRoi); + + BOOST_TEST(equal_pixels(transposed_view(const_view(in_ref)), const_view(in))); +} + +int main() +{ + test_transpose(); + test_transpose_inplace(); + + return ::boost::report_errors(); +} \ No newline at end of file From 590f04cf835d59a7ea0d5d27a4b1fb7dd0af7c43 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Fri, 16 Oct 2020 17:19:04 +0200 Subject: [PATCH 190/193] Add IPP flip benchmark --- bench/ipp/CMakeLists.txt | 8 ++++ bench/ipp/bench_flip.cpp | 97 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 bench/ipp/bench_flip.cpp diff --git a/bench/ipp/CMakeLists.txt b/bench/ipp/CMakeLists.txt index 7eb26f0ede..6835b19ba4 100644 --- a/bench/ipp/CMakeLists.txt +++ b/bench/ipp/CMakeLists.txt @@ -13,6 +13,14 @@ target_link_libraries(bench_ipp_transpose ipp::ipp ) +add_executable(bench_ipp_flip + bench_flip.cpp +) + +target_link_libraries(bench_ipp_flip + benchmark::benchmark + ipp::ipp +) add_executable(test_ipp_transpose test_transpose.cpp diff --git a/bench/ipp/bench_flip.cpp b/bench/ipp/bench_flip.cpp new file mode 100644 index 0000000000..623a7976b8 --- /dev/null +++ b/bench/ipp/bench_flip.cpp @@ -0,0 +1,97 @@ +// Copyright (C) 2020 Samuel Debionne, ESRF. + +// Use, modification and distribution is 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 + +static void ipp_flip_left_right(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + IppiSize srcRoi = { dim, dim }; + + for (auto _ : state) { + // The code to benchmark + ippiMirror_8u_C1R( + boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) in.width(), + boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), + srcRoi, + ippAxsVertical); + } +} +BENCHMARK(ipp_flip_left_right)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void ipp_flip_left_right_inplace(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + + IppiSize srcRoi = { dim, dim }; + + for (auto _ : state) { + // The code to benchmark + ippiMirror_8u_C1IR( + boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), + srcRoi, + ippAxsVertical); + } +} +BENCHMARK(ipp_flip_left_right_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void ipp_flip_up_down(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + gray8_image_t out(dim, dim); + + IppiSize srcRoi = { dim, dim }; + + for (auto _ : state) { + // The code to benchmark + ippiMirror_8u_C1R( + boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) in.width(), + boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), + srcRoi, + ippAxsHorizontal); + } +} +BENCHMARK(ipp_flip_up_down)->RangeMultiplier(2)->Range(256, 8 << 10); + +static void ipp_flip_up_down_inplace(benchmark::State& state) +{ + using namespace boost::gil; + + size_t dim = state.range(0); + + gray8_image_t in(dim, dim); + + IppiSize srcRoi = { dim, dim }; + + for (auto _ : state) { + // The code to benchmark + ippiMirror_8u_C1IR( + boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), + srcRoi, + ippAxsHorizontal); + } +} +BENCHMARK(ipp_flip_up_down_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); + +BENCHMARK_MAIN(); From 5a4c45f740e2ae40b09be0532d49942c137f2765 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Tue, 20 Oct 2020 17:29:20 +0200 Subject: [PATCH 191/193] Rebase on benchmark prototype branch --- bench/CMakeLists.txt | 62 ------------------- bench/blaze/CMakeLists.txt | 27 -------- bench/ipp/CMakeLists.txt | 31 ---------- bench/ipp/test_transpose.cpp | 58 ----------------- bench/opencv/CMakeLists.txt | 46 -------------- benchmark/google/CMakeLists.txt | 12 ++++ benchmark/google/blaze/CMakeLists.txt | 36 +++++++++++ .../google/blaze/view_transpose.cpp | 9 +++ benchmark/google/ipp/CMakeLists.txt | 33 ++++++++++ .../google/ipp/view_flip.cpp | 18 ++++++ .../google/ipp/view_transpose.cpp | 9 +++ benchmark/google/opencv/CMakeLists.txt | 40 ++++++++++++ .../google/opencv/view_flip.cpp | 0 .../google/opencv/view_rotation.cpp | 0 .../google/opencv/view_transpose.cpp | 4 +- .../google/view_flip.cpp | 0 .../google/view_rotation.cpp | 0 .../google/view_transpose.cpp | 0 .../google/view_transpose_impl.cpp | 0 19 files changed, 159 insertions(+), 226 deletions(-) delete mode 100644 bench/CMakeLists.txt delete mode 100644 bench/blaze/CMakeLists.txt delete mode 100644 bench/ipp/CMakeLists.txt delete mode 100644 bench/ipp/test_transpose.cpp delete mode 100644 bench/opencv/CMakeLists.txt create mode 100644 benchmark/google/blaze/CMakeLists.txt rename bench/blaze/bench_transpose.cpp => benchmark/google/blaze/view_transpose.cpp (77%) create mode 100644 benchmark/google/ipp/CMakeLists.txt rename bench/ipp/bench_flip.cpp => benchmark/google/ipp/view_flip.cpp (71%) rename bench/ipp/bench_transpose.cpp => benchmark/google/ipp/view_transpose.cpp (74%) create mode 100644 benchmark/google/opencv/CMakeLists.txt rename bench/opencv/bench_flip.cpp => benchmark/google/opencv/view_flip.cpp (100%) rename bench/opencv/bench_rotation.cpp => benchmark/google/opencv/view_rotation.cpp (100%) rename bench/opencv/bench_transpose.cpp => benchmark/google/opencv/view_transpose.cpp (94%) rename bench/bench_flip.cpp => benchmark/google/view_flip.cpp (100%) rename bench/bench_rotation.cpp => benchmark/google/view_rotation.cpp (100%) rename bench/bench_transpose.cpp => benchmark/google/view_transpose.cpp (100%) rename bench/bench_transpose_impl.cpp => benchmark/google/view_transpose_impl.cpp (100%) diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt deleted file mode 100644 index 033ca5826a..0000000000 --- a/bench/CMakeLists.txt +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (C) 2020 Samuel Debionne, ESRF. - -# Use, modification and distribution is 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) - -add_executable(bench_rotation - bench_rotation.cpp -) - -target_link_libraries(bench_rotation - benchmark::benchmark -) - -add_executable(bench_flip - bench_flip.cpp -) - -target_link_libraries(bench_flip - benchmark::benchmark -) - -add_executable(bench_transpose - bench_transpose.cpp -) - -target_link_libraries(bench_transpose - benchmark::benchmark -) - -add_executable(bench_transpose_impl - bench_transpose_impl.cpp -) - -target_link_libraries(bench_transpose_impl - benchmark::benchmark -) - -if(BUILD_BENCHMARK_OPENCV) - find_package(OpenCV REQUIRED) - add_subdirectory(opencv) -endif() - -if(BUILD_BENCHMARK_BLAZE) - find_package(blaze REQUIRED) - add_subdirectory(blaze) -endif() - -if(BUILD_BENCHMARK_IPP) - #find_package(IPP REQUIRED) - find_path(IPP_INCLUDE_DIR ipp.h PATHS ${IPP_ROOT}/include) - message("IPP_INCLUDE_DIR: ${IPP_INCLUDE_DIR}") - find_library(IPP_LIB_CORE ippcore PATHS ${IPP_ROOT}/lib) - find_library(IPP_LIB_I ippi PATHS ${IPP_ROOT}/lib) - message("IPP_LIB_CORE: ${IPP_LIB_CORE}") - message("IPP_LIB_I: ${IPP_LIB_I}") - add_library(ipp::ipp INTERFACE IMPORTED) - target_include_directories(ipp::ipp INTERFACE ${IPP_INCLUDE_DIR}) - target_link_libraries(ipp::ipp INTERFACE ${IPP_LIB_CORE} ${IPP_LIB_I}) - - add_subdirectory(ipp) -endif() \ No newline at end of file diff --git a/bench/blaze/CMakeLists.txt b/bench/blaze/CMakeLists.txt deleted file mode 100644 index e55e70a885..0000000000 --- a/bench/blaze/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) 2020 Samuel Debionne, ESRF. - -# Use, modification and distribution is 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) - -add_executable(bench_blaze_transpose - bench_transpose.cpp -) - -target_link_libraries(bench_blaze_transpose - benchmark::benchmark - blaze::blaze -) - -target_compile_definitions(bench_blaze_transpose - PRIVATE BLAZE_USE_SHARED_MEMORY_PARALLELIZATION=0 -) - -add_executable(bench_blaze_transpose_mt - bench_transpose.cpp -) - -target_link_libraries(bench_blaze_transpose_mt - benchmark::benchmark - blaze::blaze -) diff --git a/bench/ipp/CMakeLists.txt b/bench/ipp/CMakeLists.txt deleted file mode 100644 index 6835b19ba4..0000000000 --- a/bench/ipp/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2020 Samuel Debionne, ESRF. - -# Use, modification and distribution is 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) - -add_executable(bench_ipp_transpose - bench_transpose.cpp -) - -target_link_libraries(bench_ipp_transpose - benchmark::benchmark - ipp::ipp -) - -add_executable(bench_ipp_flip - bench_flip.cpp -) - -target_link_libraries(bench_ipp_flip - benchmark::benchmark - ipp::ipp -) - -add_executable(test_ipp_transpose - test_transpose.cpp -) - -target_link_libraries(test_ipp_transpose - ipp::ipp -) \ No newline at end of file diff --git a/bench/ipp/test_transpose.cpp b/bench/ipp/test_transpose.cpp deleted file mode 100644 index 467f05c102..0000000000 --- a/bench/ipp/test_transpose.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2020 Samuel Debionne, ESRF. - -// Use, modification and distribution is 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 - -static void test_transpose() -{ - using namespace boost::gil; - - size_t dim = 1024; - - gray8_image_t in(dim, dim); - gray8_image_t out(dim, dim); - - generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); - - IppiSize srcRoi = { dim, dim }; - ippiTranspose_8u_C1R( - boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) in.width(), - boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), - srcRoi); - - BOOST_TEST(equal_pixels(transposed_view(const_view(in)), const_view(out))); -} - -static void test_transpose_inplace() -{ - using namespace boost::gil; - - size_t dim = 1024; - - gray8_image_t in(dim, dim); - - generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); - - gray8_image_t in_ref(in); - - IppiSize srcRoi = { dim, dim }; - ippiTranspose_8u_C1IR( - boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), - srcRoi); - - BOOST_TEST(equal_pixels(transposed_view(const_view(in_ref)), const_view(in))); -} - -int main() -{ - test_transpose(); - test_transpose_inplace(); - - return ::boost::report_errors(); -} \ No newline at end of file diff --git a/bench/opencv/CMakeLists.txt b/bench/opencv/CMakeLists.txt deleted file mode 100644 index 0aabb76851..0000000000 --- a/bench/opencv/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (C) 2020 Samuel Debionne, ESRF. - -# Use, modification and distribution is 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) - -add_executable(bench_ocv_rotation - bench_rotation.cpp -) - -target_link_libraries(bench_ocv_rotation - benchmark::benchmark - ${OpenCV_LIBS} -) - -message(${OpenCV_INCLUDE_DIRS}) - -target_include_directories(bench_ocv_rotation - PRIVATE ${OpenCV_INCLUDE_DIRS} -) - -add_executable(bench_ocv_flip - bench_flip.cpp -) - -target_link_libraries(bench_ocv_flip - benchmark::benchmark - ${OpenCV_LIBS} -) - -target_include_directories(bench_ocv_flip - PRIVATE ${OpenCV_INCLUDE_DIRS} -) - -add_executable(bench_ocv_transpose - bench_transpose.cpp -) - -target_link_libraries(bench_ocv_transpose - benchmark::benchmark - ${OpenCV_LIBS} -) - -target_include_directories(bench_ocv_transpose - PRIVATE ${OpenCV_INCLUDE_DIRS} -) diff --git a/benchmark/google/CMakeLists.txt b/benchmark/google/CMakeLists.txt index 71896de43b..4bac139b2f 100644 --- a/benchmark/google/CMakeLists.txt +++ b/benchmark/google/CMakeLists.txt @@ -61,3 +61,15 @@ endforeach() unset(_benchmarks) unset(_benchmark) + +if(BOOST_GIL_BUILD_BENCHMARKS_OPENCV) + add_subdirectory(opencv) +endif() + +if(BOOST_GIL_BUILD_BENCHMARKS_BLAZE) + add_subdirectory(blaze) +endif() + +if(BOOST_GIL_BUILD_BENCHMARKS_IPP) + add_subdirectory(ipp) +endif() diff --git a/benchmark/google/blaze/CMakeLists.txt b/benchmark/google/blaze/CMakeLists.txt new file mode 100644 index 0000000000..0aa857029f --- /dev/null +++ b/benchmark/google/blaze/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2020 Samuel Debionne, ESRF. + +# Use, modification and distribution is 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) + +#----------------------------------------------------------------------------- +# Find package Blaze +#----------------------------------------------------------------------------- +find_package(blaze REQUIRED) + +#----------------------------------------------------------------------------- +# Build benchmarks +#----------------------------------------------------------------------------- +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) +else() + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) +endif() + +foreach(_benchmark ${_benchmarks}) + get_filename_component(_name ${_benchmark} NAME_WE) + set(_target benchmark_google_blaze_${_name}) + add_executable(${_target} ${_name}.cpp) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies + gil_googlebenchmark + blaze::blaze) +endforeach() + +unset(_benchmarks) +unset(_benchmark) diff --git a/bench/blaze/bench_transpose.cpp b/benchmark/google/blaze/view_transpose.cpp similarity index 77% rename from bench/blaze/bench_transpose.cpp rename to benchmark/google/blaze/view_transpose.cpp index a076877b25..9fe0783fbe 100644 --- a/bench/blaze/bench_transpose.cpp +++ b/benchmark/google/blaze/view_transpose.cpp @@ -30,6 +30,7 @@ static void blaze_transpose(benchmark::State& state) size_t dim = state.range(0); gray8_image_t in(dim, dim); + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); gray8_image_t out(dim, dim); auto mat_in = as_matrix(view(in)); @@ -39,6 +40,9 @@ static void blaze_transpose(benchmark::State& state) // The code to benchmark mat_out = blaze::trans(mat_in); } + + if (!equal_pixels(transposed_view(const_view(in)), const_view(in)))) + state.SkipWithError("blaze_transpose wrong result"); } BENCHMARK(blaze_transpose)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -49,6 +53,8 @@ static void blaze_transpose_inplace(benchmark::State& state) size_t dim = state.range(0); gray8_image_t in(dim, dim); + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); + gray8_image_t in_ref(in); auto mat_in_out = as_matrix(view(in)); @@ -56,6 +62,9 @@ static void blaze_transpose_inplace(benchmark::State& state) // The code to benchmark blaze::transpose(mat_in_out); } + + if (!equal_pixels(transposed_view(const_view(in_ref)), const_view(in)))) + state.SkipWithError("blaze_transpose_inplace wrong result"); } BENCHMARK(blaze_transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); diff --git a/benchmark/google/ipp/CMakeLists.txt b/benchmark/google/ipp/CMakeLists.txt new file mode 100644 index 0000000000..aec17fbd94 --- /dev/null +++ b/benchmark/google/ipp/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (C) 2020 Samuel Debionne, ESRF. + +# Use, modification and distribution is 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) + +#----------------------------------------------------------------------------- +# Find package IPP +#----------------------------------------------------------------------------- +#find_package(IPP REQUIRED) +find_path(IPP_INCLUDE_DIR ipp.h PATHS ${IPP_ROOT}/include) +message("IPP_INCLUDE_DIR: ${IPP_INCLUDE_DIR}") +find_library(IPP_LIB_CORE ippcore PATHS ${IPP_ROOT}/lib) +find_library(IPP_LIB_I ippi PATHS ${IPP_ROOT}/lib) +message("IPP_LIB_CORE: ${IPP_LIB_CORE}") +message("IPP_LIB_I: ${IPP_LIB_I}") +add_library(ipp::ipp INTERFACE IMPORTED) +target_include_directories(ipp::ipp INTERFACE ${IPP_INCLUDE_DIR}) +target_link_libraries(ipp::ipp INTERFACE ${IPP_LIB_CORE} ${IPP_LIB_I}) + +foreach(_benchmark ${_benchmarks}) + get_filename_component(_name ${_benchmark} NAME_WE) + set(_target benchmark_google_ipp_${_name}) + add_executable(${_target} ${_name}.cpp) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies + gil_googlebenchmark + ipp::ipp) +endforeach() diff --git a/bench/ipp/bench_flip.cpp b/benchmark/google/ipp/view_flip.cpp similarity index 71% rename from bench/ipp/bench_flip.cpp rename to benchmark/google/ipp/view_flip.cpp index 623a7976b8..36ebc6bc4a 100644 --- a/bench/ipp/bench_flip.cpp +++ b/benchmark/google/ipp/view_flip.cpp @@ -17,6 +17,7 @@ static void ipp_flip_left_right(benchmark::State& state) size_t dim = state.range(0); gray8_image_t in(dim, dim); + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); gray8_image_t out(dim, dim); IppiSize srcRoi = { dim, dim }; @@ -29,6 +30,9 @@ static void ipp_flip_left_right(benchmark::State& state) srcRoi, ippAxsVertical); } + + if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out)))) + state.SkipWithError("ipp_flip_left_right wrong result"); } BENCHMARK(ipp_flip_left_right)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -39,6 +43,8 @@ static void ipp_flip_left_right_inplace(benchmark::State& state) size_t dim = state.range(0); gray8_image_t in(dim, dim); + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); + gray8_image_t in_ref(in); IppiSize srcRoi = { dim, dim }; @@ -49,6 +55,9 @@ static void ipp_flip_left_right_inplace(benchmark::State& state) srcRoi, ippAxsVertical); } + + if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(in_ref)))) + state.SkipWithError("ipp_flip_left_right_inplace wrong result"); } BENCHMARK(ipp_flip_left_right_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -59,6 +68,7 @@ static void ipp_flip_up_down(benchmark::State& state) size_t dim = state.range(0); gray8_image_t in(dim, dim); + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); gray8_image_t out(dim, dim); IppiSize srcRoi = { dim, dim }; @@ -71,6 +81,9 @@ static void ipp_flip_up_down(benchmark::State& state) srcRoi, ippAxsHorizontal); } + + if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out)))) + state.SkipWithError("ipp_flip_up_down wrong result"); } BENCHMARK(ipp_flip_up_down)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -81,6 +94,8 @@ static void ipp_flip_up_down_inplace(benchmark::State& state) size_t dim = state.range(0); gray8_image_t in(dim, dim); + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); + gray8_image_t in_ref(in); IppiSize srcRoi = { dim, dim }; @@ -91,6 +106,9 @@ static void ipp_flip_up_down_inplace(benchmark::State& state) srcRoi, ippAxsHorizontal); } + + if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(in_ref)))) + state.SkipWithError("ipp_flip_up_down_inplace wrong result"); } BENCHMARK(ipp_flip_up_down_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); diff --git a/bench/ipp/bench_transpose.cpp b/benchmark/google/ipp/view_transpose.cpp similarity index 74% rename from bench/ipp/bench_transpose.cpp rename to benchmark/google/ipp/view_transpose.cpp index 8396f4dd10..c11bb97ca8 100644 --- a/bench/ipp/bench_transpose.cpp +++ b/benchmark/google/ipp/view_transpose.cpp @@ -17,6 +17,7 @@ static void ipp_transpose(benchmark::State& state) size_t dim = state.range(0); gray8_image_t in(dim, dim); + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); gray8_image_t out(dim, dim); IppiSize srcRoi = { dim, dim }; @@ -28,6 +29,9 @@ static void ipp_transpose(benchmark::State& state) boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), srcRoi); } + + if (!equal_pixels(transposed_view(const_view(in)), const_view(out)))) + state.SkipWithError("ipp_transpose wrong result"); } BENCHMARK(ipp_transpose)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -38,6 +42,8 @@ static void ipp_transpose_inplace(benchmark::State& state) size_t dim = state.range(0); gray8_image_t in(dim, dim); + generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); + gray8_image_t in_ref(in); IppiSize srcRoi = { dim, dim }; @@ -47,6 +53,9 @@ static void ipp_transpose_inplace(benchmark::State& state) boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), srcRoi); } + + if (!equal_pixels(transposed_view(const_view(in_ref)), const_view(in)))) + state.SkipWithError("ipp_transpose_inplace wrong result"); } BENCHMARK(ipp_transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); diff --git a/benchmark/google/opencv/CMakeLists.txt b/benchmark/google/opencv/CMakeLists.txt new file mode 100644 index 0000000000..44d13ec7ba --- /dev/null +++ b/benchmark/google/opencv/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Samuel Debionne, ESRF. + +# Use, modification and distribution is 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) + +#----------------------------------------------------------------------------- +# Find package OpenCV +#----------------------------------------------------------------------------- +find_package(OpenCV REQUIRED) + +#----------------------------------------------------------------------------- +# Build benchmarks +#----------------------------------------------------------------------------- +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) +else() + file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) +endif() + +foreach(_benchmark ${_benchmarks}) + get_filename_component(_name ${_benchmark} NAME_WE) + set(_target benchmark_google_ocv_${_name}) + add_executable(${_target} ${_name}.cpp) + target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1) + target_include_directories(${_target} + PRIVATE + ${OpenCV_INCLUDE_DIRS} + ) + target_link_libraries(${_target} + PRIVATE + gil_compile_options + gil_include_directories + gil_dependencies + gil_googlebenchmark + ${OpenCV_LIBS}) +endforeach() + +unset(_benchmarks) +unset(_benchmark) diff --git a/bench/opencv/bench_flip.cpp b/benchmark/google/opencv/view_flip.cpp similarity index 100% rename from bench/opencv/bench_flip.cpp rename to benchmark/google/opencv/view_flip.cpp diff --git a/bench/opencv/bench_rotation.cpp b/benchmark/google/opencv/view_rotation.cpp similarity index 100% rename from bench/opencv/bench_rotation.cpp rename to benchmark/google/opencv/view_rotation.cpp diff --git a/bench/opencv/bench_transpose.cpp b/benchmark/google/opencv/view_transpose.cpp similarity index 94% rename from bench/opencv/bench_transpose.cpp rename to benchmark/google/opencv/view_transpose.cpp index 26bf552a65..9bb48f0dbe 100644 --- a/bench/opencv/bench_transpose.cpp +++ b/benchmark/google/opencv/view_transpose.cpp @@ -19,7 +19,7 @@ static void transpose(benchmark::State& state) for (auto _ : state) { // The code to benchmark - transpose(in,out); + transpose(in, out); } } BENCHMARK(transpose)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -34,7 +34,7 @@ static void transpose_inplace(benchmark::State& state) for (auto _ : state) { // The code to benchmark - transpose(in,in); + transpose(in, in); } } BENCHMARK(transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); diff --git a/bench/bench_flip.cpp b/benchmark/google/view_flip.cpp similarity index 100% rename from bench/bench_flip.cpp rename to benchmark/google/view_flip.cpp diff --git a/bench/bench_rotation.cpp b/benchmark/google/view_rotation.cpp similarity index 100% rename from bench/bench_rotation.cpp rename to benchmark/google/view_rotation.cpp diff --git a/bench/bench_transpose.cpp b/benchmark/google/view_transpose.cpp similarity index 100% rename from bench/bench_transpose.cpp rename to benchmark/google/view_transpose.cpp diff --git a/bench/bench_transpose_impl.cpp b/benchmark/google/view_transpose_impl.cpp similarity index 100% rename from bench/bench_transpose_impl.cpp rename to benchmark/google/view_transpose_impl.cpp From 2c2fb7aabb3d074826185270631f9dee379d6097 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Tue, 20 Oct 2020 18:31:57 +0200 Subject: [PATCH 192/193] fixup! Rebase on benchmark prototype branch --- benchmark/google/CMakeLists.txt | 7 +++++-- benchmark/google/blaze/view_transpose.cpp | 4 ++-- benchmark/google/ipp/view_flip.cpp | 8 ++++---- benchmark/google/ipp/view_transpose.cpp | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/benchmark/google/CMakeLists.txt b/benchmark/google/CMakeLists.txt index 4bac139b2f..0e985d42bf 100644 --- a/benchmark/google/CMakeLists.txt +++ b/benchmark/google/CMakeLists.txt @@ -33,6 +33,9 @@ set_property(TARGET gil_googlebenchmark PROPERTY IMPORTED_LOCATION if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") find_library(SHLWAPI Shlwapi.lib) target_link_libraries(gil_googlebenchmark INTERFACE SHLWAPI) +else() + find_package(Threads) + target_link_libraries(gil_googlebenchmark INTERFACE ${CMAKE_THREAD_LIBS_INIT} rt) endif() add_dependencies(gil_googlebenchmark project_googlebenchmark) @@ -41,9 +44,9 @@ add_dependencies(gil_googlebenchmark project_googlebenchmark) # Build benchmarks #----------------------------------------------------------------------------- if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) - file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) + file(GLOB _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) else() - file(GLOB_RECURSE _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) + file(GLOB _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) endif() foreach(_benchmark ${_benchmarks}) diff --git a/benchmark/google/blaze/view_transpose.cpp b/benchmark/google/blaze/view_transpose.cpp index 9fe0783fbe..9d08c105d2 100644 --- a/benchmark/google/blaze/view_transpose.cpp +++ b/benchmark/google/blaze/view_transpose.cpp @@ -41,7 +41,7 @@ static void blaze_transpose(benchmark::State& state) mat_out = blaze::trans(mat_in); } - if (!equal_pixels(transposed_view(const_view(in)), const_view(in)))) + if (!equal_pixels(transposed_view(const_view(in)), const_view(in))) state.SkipWithError("blaze_transpose wrong result"); } BENCHMARK(blaze_transpose)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -63,7 +63,7 @@ static void blaze_transpose_inplace(benchmark::State& state) blaze::transpose(mat_in_out); } - if (!equal_pixels(transposed_view(const_view(in_ref)), const_view(in)))) + if (!equal_pixels(transposed_view(const_view(in_ref)), const_view(in))) state.SkipWithError("blaze_transpose_inplace wrong result"); } BENCHMARK(blaze_transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); diff --git a/benchmark/google/ipp/view_flip.cpp b/benchmark/google/ipp/view_flip.cpp index 36ebc6bc4a..e49b5ea0d8 100644 --- a/benchmark/google/ipp/view_flip.cpp +++ b/benchmark/google/ipp/view_flip.cpp @@ -31,7 +31,7 @@ static void ipp_flip_left_right(benchmark::State& state) ippAxsVertical); } - if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out)))) + if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out))) state.SkipWithError("ipp_flip_left_right wrong result"); } BENCHMARK(ipp_flip_left_right)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -56,7 +56,7 @@ static void ipp_flip_left_right_inplace(benchmark::State& state) ippAxsVertical); } - if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(in_ref)))) + if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(in_ref))) state.SkipWithError("ipp_flip_left_right_inplace wrong result"); } BENCHMARK(ipp_flip_left_right_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -82,7 +82,7 @@ static void ipp_flip_up_down(benchmark::State& state) ippAxsHorizontal); } - if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out)))) + if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out))) state.SkipWithError("ipp_flip_up_down wrong result"); } BENCHMARK(ipp_flip_up_down)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -107,7 +107,7 @@ static void ipp_flip_up_down_inplace(benchmark::State& state) ippAxsHorizontal); } - if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(in_ref)))) + if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(in_ref))) state.SkipWithError("ipp_flip_up_down_inplace wrong result"); } BENCHMARK(ipp_flip_up_down_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); diff --git a/benchmark/google/ipp/view_transpose.cpp b/benchmark/google/ipp/view_transpose.cpp index c11bb97ca8..afa0579088 100644 --- a/benchmark/google/ipp/view_transpose.cpp +++ b/benchmark/google/ipp/view_transpose.cpp @@ -30,7 +30,7 @@ static void ipp_transpose(benchmark::State& state) srcRoi); } - if (!equal_pixels(transposed_view(const_view(in)), const_view(out)))) + if (!equal_pixels(transposed_view(const_view(in)), const_view(out))) state.SkipWithError("ipp_transpose wrong result"); } BENCHMARK(ipp_transpose)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -54,7 +54,7 @@ static void ipp_transpose_inplace(benchmark::State& state) srcRoi); } - if (!equal_pixels(transposed_view(const_view(in_ref)), const_view(in)))) + if (!equal_pixels(transposed_view(const_view(in_ref)), const_view(in))) state.SkipWithError("ipp_transpose_inplace wrong result"); } BENCHMARK(ipp_transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); From 30d6c6cf5eb7e8dcc0cf760e418f43cb2939b1f6 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Fri, 28 Jul 2023 09:40:25 +0200 Subject: [PATCH 193/193] Fix IPP benchmarks --- benchmark/google/CMakeLists.txt | 8 ++++ benchmark/google/ipp/CMakeLists.txt | 12 +++++ benchmark/google/ipp/view_flip.cpp | 60 ++++++++++++++++++------- benchmark/google/ipp/view_transpose.cpp | 33 ++++++++++---- 4 files changed, 88 insertions(+), 25 deletions(-) diff --git a/benchmark/google/CMakeLists.txt b/benchmark/google/CMakeLists.txt index 0e985d42bf..77fd067acd 100644 --- a/benchmark/google/CMakeLists.txt +++ b/benchmark/google/CMakeLists.txt @@ -65,6 +65,14 @@ endforeach() unset(_benchmarks) unset(_benchmark) + +#----------------------------------------------------------------------------- +# Options +#----------------------------------------------------------------------------- +option(BOOST_GIL_BUILD_BENCHMARKS_OPENCV "Builds all OpenCV benchmarks" OFF) +option(BOOST_GIL_BUILD_BENCHMARKS_BLAZE "Builds all BLAZE benchmarks" OFF) +option(BOOST_GIL_BUILD_BENCHMARKS_IPP "Builds all IPP benchmarks" OFF) + if(BOOST_GIL_BUILD_BENCHMARKS_OPENCV) add_subdirectory(opencv) endif() diff --git a/benchmark/google/ipp/CMakeLists.txt b/benchmark/google/ipp/CMakeLists.txt index aec17fbd94..7937643805 100644 --- a/benchmark/google/ipp/CMakeLists.txt +++ b/benchmark/google/ipp/CMakeLists.txt @@ -18,6 +18,15 @@ add_library(ipp::ipp INTERFACE IMPORTED) target_include_directories(ipp::ipp INTERFACE ${IPP_INCLUDE_DIR}) target_link_libraries(ipp::ipp INTERFACE ${IPP_LIB_CORE} ${IPP_LIB_I}) +#----------------------------------------------------------------------------- +# Build benchmarks +#----------------------------------------------------------------------------- +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0) + file(GLOB _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND) +else() + file(GLOB _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp) +endif() + foreach(_benchmark ${_benchmarks}) get_filename_component(_name ${_benchmark} NAME_WE) set(_target benchmark_google_ipp_${_name}) @@ -31,3 +40,6 @@ foreach(_benchmark ${_benchmarks}) gil_googlebenchmark ipp::ipp) endforeach() + +unset(_benchmarks) +unset(_benchmark) diff --git a/benchmark/google/ipp/view_flip.cpp b/benchmark/google/ipp/view_flip.cpp index e49b5ea0d8..8a3b67ad11 100644 --- a/benchmark/google/ipp/view_flip.cpp +++ b/benchmark/google/ipp/view_flip.cpp @@ -10,6 +10,8 @@ #include +inline bool is_odd(benchmark::IterationCount cnt) { return (cnt % 2); } + static void ipp_flip_left_right(benchmark::State& state) { using namespace boost::gil; @@ -24,11 +26,14 @@ static void ipp_flip_left_right(benchmark::State& state) for (auto _ : state) { // The code to benchmark - ippiMirror_8u_C1R( - boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) in.width(), - boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), + auto res = ippiMirror_8u_C1R( + boost::gil::interleaved_view_get_raw_data(const_view(in)), (int)const_view(in).pixels().row_size(), + boost::gil::interleaved_view_get_raw_data(view(out)), (int)view(in).pixels().row_size(), srcRoi, ippAxsVertical); + + if (res != ippStsNoErr) + state.SkipWithError("ipp_flip_left_right failed"); } if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out))) @@ -50,14 +55,23 @@ static void ipp_flip_left_right_inplace(benchmark::State& state) for (auto _ : state) { // The code to benchmark - ippiMirror_8u_C1IR( - boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), + auto res = ippiMirror_8u_C1IR( + boost::gil::interleaved_view_get_raw_data(view(in)), (int)view(in).pixels().row_size(), srcRoi, ippAxsVertical); + + if (res != ippStsNoErr) + state.SkipWithError("ipp_flip_left_right_inplace failed"); + } + + if (is_odd(state.iterations())) { + if (!equal_pixels(flipped_left_right_view(const_view(in_ref)), const_view(in))) + state.SkipWithError("ipp_flip_left_right_inplace wrong result"); + } + else { + if (!equal_pixels(const_view(in_ref), const_view(in))) + state.SkipWithError("ipp_flip_left_right_inplace wrong result"); } - - if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(in_ref))) - state.SkipWithError("ipp_flip_left_right_inplace wrong result"); } BENCHMARK(ipp_flip_left_right_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -75,15 +89,24 @@ static void ipp_flip_up_down(benchmark::State& state) for (auto _ : state) { // The code to benchmark - ippiMirror_8u_C1R( - boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) in.width(), - boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), + auto res = ippiMirror_8u_C1R( + boost::gil::interleaved_view_get_raw_data(const_view(in)), (int)const_view(in).pixels().row_size(), + boost::gil::interleaved_view_get_raw_data(view(out)), (int)view(out).pixels().row_size(), srcRoi, ippAxsHorizontal); + + if (res != ippStsNoErr) + state.SkipWithError("ipp_flip_up_down failed"); + } + + if (is_odd(state.iterations())) { + if (!equal_pixels(flipped_up_down_view(const_view(in)), const_view(out))) + state.SkipWithError("ipp_flip_up_down wrong result"); + } + else { + if (!equal_pixels(const_view(in), const_view(out))) + state.SkipWithError("ipp_flip_up_down wrong result"); } - - if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out))) - state.SkipWithError("ipp_flip_up_down wrong result"); } BENCHMARK(ipp_flip_up_down)->RangeMultiplier(2)->Range(256, 8 << 10); @@ -101,13 +124,16 @@ static void ipp_flip_up_down_inplace(benchmark::State& state) for (auto _ : state) { // The code to benchmark - ippiMirror_8u_C1IR( - boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), + auto res = ippiMirror_8u_C1IR( + boost::gil::interleaved_view_get_raw_data(view(in)), (int)view(in).pixels().row_size(), srcRoi, ippAxsHorizontal); + + if (res != ippStsNoErr) + state.SkipWithError("ipp_flip_up_down_inplace failed"); } - if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(in_ref))) + if (!equal_pixels(flipped_up_down_view(const_view(in_ref)), const_view(in))) state.SkipWithError("ipp_flip_up_down_inplace wrong result"); } BENCHMARK(ipp_flip_up_down_inplace)->RangeMultiplier(2)->Range(256, 8 << 10); diff --git a/benchmark/google/ipp/view_transpose.cpp b/benchmark/google/ipp/view_transpose.cpp index afa0579088..037dc8b92b 100644 --- a/benchmark/google/ipp/view_transpose.cpp +++ b/benchmark/google/ipp/view_transpose.cpp @@ -10,6 +10,8 @@ #include +inline bool is_odd(benchmark::IterationCount cnt) { return (cnt % 2); } + static void ipp_transpose(benchmark::State& state) { using namespace boost::gil; @@ -24,10 +26,13 @@ static void ipp_transpose(benchmark::State& state) for (auto _ : state) { // The code to benchmark - ippiTranspose_8u_C1R( - boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) in.width(), - boost::gil::interleaved_view_get_raw_data(view(out)), (int) out.width(), + auto res = ippiTranspose_8u_C1R( + boost::gil::interleaved_view_get_raw_data(const_view(in)), (int) const_view(in).pixels().row_size(), + boost::gil::interleaved_view_get_raw_data(view(out)), (int)const_view(out).pixels().row_size(), srcRoi); + + if (res != ippStsNoErr) + state.SkipWithError("ipp_transpose_inplace failed"); } if (!equal_pixels(transposed_view(const_view(in)), const_view(out))) @@ -45,17 +50,29 @@ static void ipp_transpose_inplace(benchmark::State& state) generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; }); gray8_image_t in_ref(in); + if (!equal_pixels(const_view(in_ref), const_view(in))) + state.SkipWithError("ipp_transpose_inplace wrong init"); + IppiSize srcRoi = { dim, dim }; for (auto _ : state) { // The code to benchmark - ippiTranspose_8u_C1IR( - boost::gil::interleaved_view_get_raw_data(view(in)), (int) in.width(), + auto res = ippiTranspose_8u_C1IR( + boost::gil::interleaved_view_get_raw_data(view(in)), (int)const_view(in).pixels().row_size(), srcRoi); + + if (res != ippStsNoErr) + state.SkipWithError("ipp_transpose_inplace failed"); + } + + if (is_odd(state.iterations())) { + if (!equal_pixels(transposed_view(const_view(in_ref)), const_view(in))) + state.SkipWithError("ipp_transpose_inplace wrong result"); + } + else { + if (!equal_pixels(const_view(in_ref), const_view(in))) + state.SkipWithError("ipp_transpose_inplace wrong result"); } - - if (!equal_pixels(transposed_view(const_view(in_ref)), const_view(in))) - state.SkipWithError("ipp_transpose_inplace wrong result"); } BENCHMARK(ipp_transpose_inplace)->RangeMultiplier(2)->Range(256, 8 << 10);