From 7e87ecd7e3858c2d8a79b05f243849366829d98a Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Tue, 5 Jul 2022 17:53:30 +0200 Subject: [PATCH 01/37] fix: wrong return type of copy_fn and missing typedef in destruct_range --- include/boost/gil/algorithm.hpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/include/boost/gil/algorithm.hpp b/include/boost/gil/algorithm.hpp index 2b61068830..6f5a0013e9 100644 --- a/include/boost/gil/algorithm.hpp +++ b/include/boost/gil/algorithm.hpp @@ -143,30 +143,24 @@ namespace std { template BOOST_FORCEINLINE auto copy( - boost::gil::pixel* first, - boost::gil::pixel* last, + boost::gil::pixel const* first, + boost::gil::pixel const* last, boost::gil::pixel* dst) -> boost::gil::pixel* { - auto p = std::copy((unsigned char*)first, (unsigned char*)last, (unsigned char*)dst); + auto p = std::copy( + reinterpret_cast(first), + reinterpret_cast(last), + reinterpret_cast(dst)); return reinterpret_cast*>(p); } -/// \ingroup STLOptimizations -/// \brief Copy when both src and dst are interleaved and of the same type can be just memmove -template -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 namespace boost { namespace gil { namespace detail { template struct copy_fn { - BOOST_FORCEINLINE I operator()(I first, I last, O dst) const { return std::copy(first,last,dst); } + BOOST_FORCEINLINE O operator()(I first, I last, O dst) const { return std::copy(first,last,dst); } }; } // namespace detail } } // namespace boost::gil @@ -454,8 +448,9 @@ void destruct_range_impl(Iterator first, Iterator last, detail::is_trivially_destructible::value_type> > >::value - >::type* /*ptr*/ = 0) + >::type* /* ptr */ = nullptr) { + using value_t = typename std::iterator_traits::value_type; while (first != last) { first->~value_t(); From 4d97acb46c81851057d035c5b3e32f93df78a541 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 2 Sep 2026 16:23:08 +0200 Subject: [PATCH 02/37] fix: Remove UB std::floating_point specialization --- .../gil/extension/io/tiff/detail/is_allowed.hpp | 2 +- include/boost/gil/io/typedefs.hpp | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/boost/gil/extension/io/tiff/detail/is_allowed.hpp b/include/boost/gil/extension/io/tiff/detail/is_allowed.hpp index f260197f52..71900843a8 100644 --- a/include/boost/gil/extension/io/tiff/detail/is_allowed.hpp +++ b/include/boost/gil/extension/io/tiff/detail/is_allowed.hpp @@ -98,7 +98,7 @@ struct Format_Type < typename is_bit_aligned::type>::type >, - std::is_floating_point + is_floating_point >::value >::type > diff --git a/include/boost/gil/io/typedefs.hpp b/include/boost/gil/io/typedefs.hpp index 78c8bc40eb..daac1348d2 100644 --- a/include/boost/gil/io/typedefs.hpp +++ b/include/boost/gil/io/typedefs.hpp @@ -29,12 +29,19 @@ using byte_vector_t = std::vector; }} // namespace boost::gil -namespace std { +namespace boost { namespace gil { namespace detail { -template<> struct is_floating_point<::boost::gil::float32_t> : std::true_type {}; -template<> struct is_floating_point<::boost::gil::float64_t> : std::true_type {}; +// Specializing std::is_floating_point for program-defined types is +// undefined behavior (Clause 20 [meta.type.synop]), so GIL IO code that +// needs to know whether a channel type is a floating point type (e.g. +// TIFF SAMPLEFORMAT detection) should use this trait instead. +template +struct is_floating_point : std::is_floating_point {}; -} // namespace std +template<> struct is_floating_point : std::true_type {}; +template<> struct is_floating_point : std::true_type {}; + +}}} // namespace boost::gil::detail namespace boost { namespace gil { From 42016875382a0d5e1ae47c71133e518d74c225ee Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 2 Sep 2026 20:20:44 +0200 Subject: [PATCH 03/37] Fix io::tiff::console_log using snprintf instead of vsnprintf --- include/boost/gil/extension/io/tiff/detail/log.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/gil/extension/io/tiff/detail/log.hpp b/include/boost/gil/extension/io/tiff/detail/log.hpp index bed9978f9b..e55d837fc1 100644 --- a/include/boost/gil/extension/io/tiff/detail/log.hpp +++ b/include/boost/gil/extension/io/tiff/detail/log.hpp @@ -45,7 +45,7 @@ class console_log ) { char buf[1000]; - snprintf(buf, 1000, fmt, ap); + vsnprintf(buf, sizeof(buf), fmt, ap); std::cout << "error: " << buf << std::endl; } @@ -55,8 +55,8 @@ class console_log ) { char buf[1000]; - snprintf(buf, 1000, fmt, ap); - std::cout << "warning: " << fmt << std::endl; + vsnprintf(buf, sizeof(buf), fmt, ap); + std::cout << "warning: " << buf << std::endl; } }; From 4664a7e907af4d538600f1ad86b2ed5283d5ff04 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 2 Sep 2026 20:40:27 +0200 Subject: [PATCH 04/37] Fix integer overflow in channel_converter_unsigned_integral_nondivisible --- include/boost/gil/channel_algorithm.hpp | 11 ++++--- .../channel/algorithm_channel_convert.cpp | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/include/boost/gil/channel_algorithm.hpp b/include/boost/gil/channel_algorithm.hpp index 447056d186..1ac5214c1c 100644 --- a/include/boost/gil/channel_algorithm.hpp +++ b/include/boost/gil/channel_algorithm.hpp @@ -267,10 +267,13 @@ struct channel_converter_unsigned_integral_nondivisible DstChannelV { - using dest_t = typename base_channel_type::type; - return DstChannelV( - static_cast(src * unsigned_integral_max_value::value) - / unsigned_integral_max_value::value); + // The mp_less check that selects this specialization guarantees that + // (src_max * dst_max) fits in uintmax_t, so the whole expression must + // be computed in a type at least that wide -- not in dest_t, which is + // only sized to hold dst_max and can be much narrower. + return DstChannelV(static_cast( + static_cast(src) * unsigned_integral_max_value::value + / unsigned_integral_max_value::value)); } }; diff --git a/test/core/channel/algorithm_channel_convert.cpp b/test/core/channel/algorithm_channel_convert.cpp index e39555b65a..6dc353114a 100644 --- a/test/core/channel/algorithm_channel_convert.cpp +++ b/test/core/channel/algorithm_channel_convert.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "test_fixture.hpp" @@ -240,6 +241,33 @@ struct test_channel_reference_const_convert_from_float64_t } }; +//--- Regression test: channel_convert must not overflow when the intermediate +// product (src * dst_max) does not fit in the destination's own storage +// type, only in a full uintmax_t. This hits +// channel_converter_unsigned_integral_nondivisible, +// selected when src_max < dst_max, dst_max is not a multiple of src_max, +// and bits(Src) + bits(Dst) <= bits(uintmax_t). +void test_channel_convert_nondivisible_no_overflow() +{ + using src_t = std::uint16_t; + using dst_t = gil::packed_channel_value<24>; + + // 65535 * 16777215 ~= 1.1e12: overflows a 32-bit intermediate (the size of + // dst_t's own storage), but fits in uintmax_t. + dst_t const dst_from_max = gil::channel_convert((std::numeric_limits::max)()); + BOOST_TEST_EQ( + static_cast(dst_from_max), + static_cast(gil::channel_traits::max_value())); + + src_t const src_mid = 12345; + dst_t const dst_from_mid = gil::channel_convert(src_mid); + std::uintmax_t const expected = + static_cast(src_mid) + * gil::channel_traits::max_value() + / gil::channel_traits::max_value(); + BOOST_TEST_EQ(static_cast(dst_from_mid), expected); +} + int main() { test_channel_value_convert_from_uint8_t::run(); @@ -249,6 +277,8 @@ int main() test_channel_value_convert_from_uint32_t::run(); test_channel_value_convert_from_int32_t::run(); + test_channel_convert_nondivisible_no_overflow(); + test_channel_value_convert_from_float32_t::run(); test_channel_reference_convert_from_float32_t::run(); test_channel_reference_const_from_float32_t::run(); From d7443be70535ab8ac73df82b1aa957a03eb417fd Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Mon, 15 Mar 2021 17:54:24 +0530 Subject: [PATCH 05/37] Add image sharpening algorithm --- example/sharpening.cpp | 27 +++ .../boost/gil/image_processing/sharpening.hpp | 189 ++++++++++++++++++ test/core/image_processing/CMakeLists.txt | 3 +- test/core/image_processing/Jamfile | 1 + test/core/image_processing/sharpening.cpp | 93 +++++++++ 5 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 example/sharpening.cpp create mode 100644 include/boost/gil/image_processing/sharpening.hpp create mode 100644 test/core/image_processing/sharpening.cpp diff --git a/example/sharpening.cpp b/example/sharpening.cpp new file mode 100644 index 0000000000..e98ce9d1c1 --- /dev/null +++ b/example/sharpening.cpp @@ -0,0 +1,27 @@ +// +// Copyright 2021 Harsit Pant +// +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#include +#include +#include +#include + +using namespace boost::gil; +using namespace std; +int main() +{ + gray8_image_t img_in; + read_image("test_adaptive.png", img_in, png_tag{}); + gray8_image_t img_out(img_in.dimensions()); + + sharpen(view(img_in), view(img_out), 1, 3); + write_view("sharpened image.png", view(img_out), png_tag{}); + + cout << "done"; + cin.get(); + return 0; +} diff --git a/include/boost/gil/image_processing/sharpening.hpp b/include/boost/gil/image_processing/sharpening.hpp new file mode 100644 index 0000000000..b244caf8a6 --- /dev/null +++ b/include/boost/gil/image_processing/sharpening.hpp @@ -0,0 +1,189 @@ +// +// Copyright 2021 Harsit Pant +// +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_GIL_IMAGE_PROCESSING_SHARPENING_HPP +#define BOOST_GIL_IMAGE_PROCESSING_SHARPENING_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace boost { namespace gil { +namespace detail{ + +/// \breif USM algorithm might sharpen noise (perceiving noise as edges), to control this +/// threshold is specified, thus sharpening only high frequency regions (strong edges). +/// For each pixel I(u,v), if (local_contrast(u, v) < minimum local contrast), put I(u, v) = 0. +template +void threshold_mask(View const& highpass_filtered_image, double threshold) +{ + float32_t max_absolute_contrast = 0.0; + for_each_pixel(nth_channel_view(highpass_filtered_image, 0), + [&max_absolute_contrast](auto& pixel) { + if (max_absolute_contrast < std::abs(pixel[0])) + max_absolute_contrast = std::abs(pixel[0]); }); + + float32_t const minimum_local_contrat = max_absolute_contrast * static_cast(threshold); + + for_each_pixel(nth_channel_view(highpass_filtered_image, 0), + [&minimum_local_contrat](auto& pixel) { + if (std::abs(pixel[0]) < minimum_local_contrat) + pixel[0] = 0; }); +} + +/// \breif clamps lab colors out of gamut for RGB color space. +template +inline void clip_lab_color(SrcView& float32_output_image) +{ + rgb32f_pixel_t rgb32f_temp_pixel; + + for_each_pixel(float32_output_image, [&rgb32f_temp_pixel](auto& input_pixel) { + + color_convert(input_pixel, rgb32f_temp_pixel); + + if (rgb32f_temp_pixel[0] > 1) rgb32f_temp_pixel[0] = 1; + if (rgb32f_temp_pixel[0] < 0) rgb32f_temp_pixel[0] = 0; + if (rgb32f_temp_pixel[1] > 1) rgb32f_temp_pixel[1] = 1; + if (rgb32f_temp_pixel[1] < 0) rgb32f_temp_pixel[1] = 0; + if (rgb32f_temp_pixel[2] > 1) rgb32f_temp_pixel[2] = 1; + if (rgb32f_temp_pixel[2] < 0) rgb32f_temp_pixel[2] = 0; + + color_convert(rgb32f_temp_pixel, input_pixel); }); +} + +/// TODO - Replace use of this function with convolve_2d when, +/// it starts supporting boundary_option::extend_constant. +template +void convolve_2d_extend_constant(SrcView const& src_view, DstView const& dst_view, + Kernel const& kernel) +{ + auto padded_image = extend_boundary(src_view, kernel.size() / 2, + boundary_option::extend_constant); + + decltype(padded_image) convolved_image(padded_image.dimensions()); + + detail::convolve_2d(view(padded_image), kernel, view(convolved_image)); + + copy_pixels(subimage_view(view(convolved_image), + point_t(kernel.size() / 2, kernel.size() / 2), + src_view.dimensions()), dst_view); +} +} // namespace detail + +/// \addtogroup ImageProcessing +/// @{ +/// \breif Sharpens grayscale/RGB images using unsharp masking technique. +/// +/// For each pixel I(u, v) - +/// if(local_contrast(u, v) > minimum local constrast) +/// I(u, v) = I(u, v) + amount * (I(u, v) - I'(u, v)) +/// else +/// I(u, v) = I (u, v) +/// where, minimum local contrast is specified using the parameter threshold. +/// I' is a smoother version of I, made by gaussian bluring. +/// +/// RGB color images are converted to lab color space, USM algorithm is applied to L* channel +/// (a*and b* channels remain unchanged), then lab image is converted back to RGB color space. +/// +/// Reference - Principles of Digital Image Processing - Fundamental Techniques +/// by Wilhelm Burger, Mark J.Burge. +/// +/// \param src_view - Source image view. +/// \param dst_view - Destination image view. +/// \param sigma - Standard deviation for generating gaussian kernel. +/// \param amount - Sharpening amount or weight. +/// \param threshold - Minimum local contrast for each pixel, +/// specified in the range 0 to 1, to apply sharpening. +/// \tparam SrcView - Source image view type. +/// \tparam DstView - Destination image view type. +template +void sharpen(SrcView const& src_view, DstView const& dst_view, double sigma = 1, + double amount = 1, double threshold = 0.0) +{ + gil_function_requires>(); + gil_function_requires>(); + + using SrcColorSpace = typename color_space_type::type; + using DstColorSpace = typename color_space_type::type; + + static_assert(color_spaces_are_compatible::value, + "Source and destination views must have same color space."); + + static_assert(is_same::value || + is_same::value, + "Incompatible Color space used."); + + BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + + int const kernel_size = static_cast(std::ceil(sigma) * 6 + 1); + detail::kernel_2d highpass_kernel = generate_gaussian_kernel + (kernel_size, static_cast(sigma)); + + //highpass kernel = impulse kernel - lowpass kernel. Produces the mask. + for_each(highpass_kernel.begin(), highpass_kernel.end(), [](float& x) { x *= -1; }); + highpass_kernel[kernel_size / 2 * (kernel_size + 1)] += 1; + + typename std::conditional< + is_same::value, + lab32f_image_t, gray32f_image_t>::type float32_input_image(src_view.dimensions()), + float32_output_image(src_view.dimensions()); + + copy_and_convert_pixels(src_view, view(float32_input_image)); + copy_and_convert_pixels(src_view, view(float32_output_image)); + + detail::convolve_2d_extend_constant(nth_channel_view(view(float32_input_image), 0), + nth_channel_view(view(float32_output_image), 0), + highpass_kernel); + + if (threshold > 0.0f) + { + detail::threshold_mask(view(float32_output_image), threshold); + } + + transform_pixels(view(float32_input_image), + view(float32_output_image), + view(float32_output_image), [&amount](auto orignal, auto mask) { + decltype(orignal) temp(orignal); + temp[0] += mask[0] * + static_cast(amount); + return temp; }); + + float32_t channel_max = (is_same::value) ? 100 : 1; + float32_t channel_min = 0; + for_each_pixel(nth_channel_view(view(float32_output_image), 0), + [&channel_max, &channel_min](auto& pixel) { + if (pixel[0] > channel_max) + pixel[0] = channel_max; + if (pixel[0] < channel_min) + pixel[0] = channel_min; }); + + if (is_same::value) + { + clip_lab_color(view(float32_output_image)); + } + + copy_and_convert_pixels(view(float32_output_image), dst_view); +} +/// @} + +}} //namespace boost::gil +#endif //BOOST_GIL_IMAGE_PROCESSING_SHARPENING_HPP diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index a2a8e08fbc..65fa032fd9 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -24,7 +24,8 @@ foreach(_name convolve_cols convolve_rows kernel - kernel_fixed) + kernel_fixed + sharpening) 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 562860e811..7bf11a87d3 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -28,3 +28,4 @@ run convolve_cols.cpp ; run convolve_rows.cpp ; run kernel.cpp ; run kernel_fixed.cpp ; +run sharpening.cpp ; diff --git a/test/core/image_processing/sharpening.cpp b/test/core/image_processing/sharpening.cpp new file mode 100644 index 0000000000..f5f149c05b --- /dev/null +++ b/test/core/image_processing/sharpening.cpp @@ -0,0 +1,93 @@ +// +// Copyright 2021 Harsit Pant +// +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#include +#include +#include + +#include + +using namespace boost; +using namespace gil; +using namespace std; + +int const width = 9; +int const height = 9; + +gray8_image_t gray_img_in(width, height); +gray8_image_t gray_img_out(width, height); +gray8_image_t gray_img_expected(width, height); + +rgb8_image_t rgb_img_in(width, height); +rgb8_image_t rgb_img_out(width, height); +rgb8_image_t rgb_img_expected(width, height); + +void no_edges() +{ + //all pixels same, no edge detected, thus nothing to sharpen. + fill_pixels(view(gray_img_in), gray8_pixel_t(50)); + fill_pixels(view(gray_img_expected), gray8_pixel_t(50)); + + sharpen(const_view(gray_img_in), view(gray_img_out), 2, 1, 0.5); + + BOOST_TEST(equal_pixels(const_view(gray_img_out), const_view(gray_img_expected))); +} + +void no_edges_rgb() +{ + //all pixels same, no edge detected, thus nothing to sharpen. + fill_pixels(view(rgb_img_in), rgb8_pixel_t(50, 50, 50)); + fill_pixels(view(rgb_img_expected), rgb8_pixel_t(50, 50, 50)); + + sharpen(const_view(rgb_img_in), view(rgb_img_out), 1, 3, 0.1); + + BOOST_TEST(equal_pixels(const_view(rgb_img_out), const_view(rgb_img_expected))); +} + +void amount_zero() +{ + // image remains unchanged if specified ammount is zero. + uint8_t in[] = + { + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53 + }; + + uint8_t out[] = + { + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53, + 12, 32, 224, 53, 255, 55, 32, 224, 53 + }; + + gray8c_view_t in_view = interleaved_view(9, 9, reinterpret_cast(in), 9); + + gray8c_view_t exp_view = interleaved_view(9, 9, reinterpret_cast(out), 9); + + sharpen(in_view, view(gray_img_out), 1, 0); + BOOST_TEST(equal_pixels(const_view(gray_img_out), exp_view)); +} +int main() +{ + no_edges(); + no_edges_rgb(); + amount_zero(); + return ::boost::report_errors(); +} From 646df18d74c678902e620719783574de57e0c641 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 2 Sep 2026 21:37:09 +0200 Subject: [PATCH 06/37] Fix missing Boost::stl_interfaces dependency in CMakeLists.txt boost/gil/pixel_iterator_adaptor.hpp includes boost/stl_interfaces/iterator_interface.hpp, but the boost_gil target's link libraries never declared Boost::stl_interfaces. Building gil via a minimal CMake superbuild (BOOST_INCLUDE_LIBRARIES=gil) therefore fails to pull in stl_interfaces at all, causing a missing-header error. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index dae5c58233..cd433a2d68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ target_link_libraries(boost_gil Boost::iterator Boost::mp11 Boost::preprocessor + Boost::stl_interfaces Boost::type_traits Boost::variant2 ) From 333eeac3681fc37688b652d1658a7585010bfba3 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 2 Sep 2026 21:58:21 +0200 Subject: [PATCH 07/37] Add more sharpening regression tests --- test/core/image_processing/sharpening.cpp | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/test/core/image_processing/sharpening.cpp b/test/core/image_processing/sharpening.cpp index f5f149c05b..723e08ad76 100644 --- a/test/core/image_processing/sharpening.cpp +++ b/test/core/image_processing/sharpening.cpp @@ -84,10 +84,88 @@ void amount_zero() sharpen(in_view, view(gray_img_out), 1, 0); BOOST_TEST(equal_pixels(const_view(gray_img_out), exp_view)); } + +void spike_produces_halo() +{ + // Sharpening a single bright spike on a flat background must boost the + // spike itself and push its immediate neighbours *below* the background + // level (the classic unsharp-mask overshoot/undershoot halo). Unlike the + // tests above, this exercises an input where the correct output is + // actually different from the input. + int const w = 15; + int const h = 3; + + gray8_image_t in(w, h); + gray8_image_t out(w, h); + fill_pixels(view(in), gray8_pixel_t(50)); + for (int y = 0; y < h; ++y) + view(in)(w / 2, y) = gray8_pixel_t(200); + + sharpen(const_view(in), view(out), 1.0, 1.0, 0.0); + + uint8_t expected[] = + { + 50, 50, 50, 50, 49, 42, 14, 255, 14, 42, 49, 50, 50, 50, 50, + 50, 50, 50, 50, 49, 42, 14, 255, 14, 42, 49, 50, 50, 50, 50, + 50, 50, 50, 50, 49, 42, 14, 255, 14, 42, 49, 50, 50, 50, 50 + }; + gray8c_view_t exp_view = interleaved_view(w, h, reinterpret_cast(expected), w); + + BOOST_TEST(equal_pixels(const_view(out), exp_view)); +} + +void threshold_suppresses_weak_edge() +{ + // Two edges of different strength: a weak one (50 -> 70) and a strong + // one (50 -> 250). With threshold = 0.5, only the strong edge (whose + // contrast exceeds half of the image's maximum contrast) should be + // sharpened; the weak edge must come out byte-for-byte identical to the + // input. + int const w = 40; + int const h = 3; + + gray8_image_t in(w, h); + gray8_image_t out(w, h); + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + uint8_t v; + if (x < w / 2) + v = (x < w / 4) ? 50 : 70; + else + v = (x < 3 * w / 4) ? 50 : 250; + view(in)(x, y) = gray8_pixel_t(v); + } + } + + sharpen(const_view(in), view(out), 1.0, 1.0, 0.5); + + uint8_t expected[] = + { + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 255, 250, 250, 250, 250, 250, 250, 250, 250, 250, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 255, 250, 250, 250, 250, 250, 250, 250, 250, 250, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 255, 250, 250, 250, 250, 250, 250, 250, 250, 250 + }; + gray8c_view_t exp_view = interleaved_view(w, h, reinterpret_cast(expected), w); + + // The weak edge (columns [0, w/2)) must be completely unaffected. + BOOST_TEST(equal_pixels( + subimage_view(const_view(out), 0, 0, w / 2, h), + subimage_view(const_view(in), 0, 0, w / 2, h))); + + BOOST_TEST(equal_pixels(const_view(out), exp_view)); +} + int main() { no_edges(); no_edges_rgb(); amount_zero(); + spike_produces_halo(); + threshold_suppresses_weak_edge(); return ::boost::report_errors(); } From 875399586fd89bf52944b8b277b485244d5e888d Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 2 Sep 2026 22:01:48 +0200 Subject: [PATCH 08/37] Remove deprecated headers now included in algorithm.hpp --- include/boost/gil/image_processing/sharpening.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/boost/gil/image_processing/sharpening.hpp b/include/boost/gil/image_processing/sharpening.hpp index b244caf8a6..7b9e277c16 100644 --- a/include/boost/gil/image_processing/sharpening.hpp +++ b/include/boost/gil/image_processing/sharpening.hpp @@ -8,9 +8,6 @@ #ifndef BOOST_GIL_IMAGE_PROCESSING_SHARPENING_HPP #define BOOST_GIL_IMAGE_PROCESSING_SHARPENING_HPP -#include -#include -#include #include #include From cb90d789a19bea686b7d20b7e24865f44b4f806d Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 2 Sep 2026 22:04:38 +0200 Subject: [PATCH 09/37] Add precondition for sigma (must be greater than 0) --- include/boost/gil/image_processing/sharpening.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/gil/image_processing/sharpening.hpp b/include/boost/gil/image_processing/sharpening.hpp index 7b9e277c16..91c5a488a7 100644 --- a/include/boost/gil/image_processing/sharpening.hpp +++ b/include/boost/gil/image_processing/sharpening.hpp @@ -106,7 +106,7 @@ void convolve_2d_extend_constant(SrcView const& src_view, DstView const& dst_vie /// /// \param src_view - Source image view. /// \param dst_view - Destination image view. -/// \param sigma - Standard deviation for generating gaussian kernel. +/// \param sigma - Standard deviation for generating gaussian kernel. Must be greater than 0. /// \param amount - Sharpening amount or weight. /// \param threshold - Minimum local contrast for each pixel, /// specified in the range 0 to 1, to apply sharpening. @@ -130,6 +130,7 @@ void sharpen(SrcView const& src_view, DstView const& dst_view, double sigma = 1, "Incompatible Color space used."); BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + BOOST_ASSERT(sigma > 0.0); int const kernel_size = static_cast(std::ceil(sigma) * 6 + 1); detail::kernel_2d highpass_kernel = generate_gaussian_kernel From 1081c0b6199d811c196ad3fcc8d4dd87c02e8bf3 Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Sat, 3 Apr 2021 10:20:36 +0530 Subject: [PATCH 10/37] Add distance transform functionality --- example/euclidean_distance_transform.cpp | 81 +++ .../image_processing/distance_transform.hpp | 517 ++++++++++++++++++ test/core/image_processing/CMakeLists.txt | 3 +- test/core/image_processing/Jamfile | 1 + .../image_processing/distance_transform.cpp | 480 ++++++++++++++++ 5 files changed, 1081 insertions(+), 1 deletion(-) create mode 100644 example/euclidean_distance_transform.cpp create mode 100644 include/boost/gil/image_processing/distance_transform.hpp create mode 100644 test/core/image_processing/distance_transform.cpp diff --git a/example/euclidean_distance_transform.cpp b/example/euclidean_distance_transform.cpp new file mode 100644 index 0000000000..2869ccbe9e --- /dev/null +++ b/example/euclidean_distance_transform.cpp @@ -0,0 +1,81 @@ +// +// Copyright 2021 Harsit Pant +// +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#include +#include +#include +#include + +#include + +namespace gil = boost::gil; + +/// calculates distance transform based on euclidean distance measurement formulae. + +int main(int argc, char* argv[]) +{ + if (argc != 6) + { + std::cerr << "usage: " << argv[0] << " " + " "; + return -1; + } + + std::string dist_from = argv[3]; + std::string dist_type = argv[4]; + std::string mask_size = argv[5]; + + gil::gray8_image_t input; + gil::read_image(argv[1], input, gil::png_tag{}); + gil::gray8_image_t output(input.dimensions()); + + if (dist_from == "on_pixels" && dist_type == "euclidean_approximation" && mask_size == "three") + { + distance_transform(view(input), view(output), gil::distance_from::on_pixels, + gil::distance_type::euclidean_approximation, gil::mask_size::three); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if (dist_from == "off_pixels" && dist_type == "euclidean_approximation" && mask_size == "three") + { + distance_transform(view(input), view(output), gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, gil::mask_size::three); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if (dist_from == "on_pixels" && dist_type == "euclidean_approximation" && mask_size == "five") + { + distance_transform(view(input), view(output), gil::distance_from::on_pixels, + gil::distance_type::euclidean_approximation, gil::mask_size::five); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if (dist_from == "off_pixels" && dist_type == "euclidean_approximation" && mask_size == "five") + { + distance_transform(view(input), view(output), gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, gil::mask_size::five); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if (dist_from == "on_pixels" && dist_type == "precise_euclidean" && mask_size == "not_applicable") + { + distance_transform(view(input), view(output), gil::distance_from::on_pixels, + gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + + else if (dist_from == "off_pixels" && dist_type == "precise_euclidean" && mask_size == "not_applicable") + { + distance_transform(view(input), view(output), gil::distance_from::off_pixels, + gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + gil::write_view(argv[2], view(output), gil::png_tag{}); + } + else + { + std::cerr << "Incorrect options used."; + } +} diff --git a/include/boost/gil/image_processing/distance_transform.hpp b/include/boost/gil/image_processing/distance_transform.hpp new file mode 100644 index 0000000000..188ead88f1 --- /dev/null +++ b/include/boost/gil/image_processing/distance_transform.hpp @@ -0,0 +1,517 @@ +// +// Copyright 2021 Harsit Pant +// +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_GIL_IMAGE_PROCESSING_DISTANCE_TRANSFORM_HPP +#define BOOST_GIL_IMAGE_PROCESSING_DISTANCE_TRANSFORM_HPP + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace boost { namespace gil { + +namespace distance_type{ +struct euclidean_approximation_t {}; +struct manhattan_t {}; +struct chessboard_t {}; +struct precise_euclidean_t {}; + +static euclidean_approximation_t euclidean_approximation; +static manhattan_t manhattan; +static chessboard_t chessboard; +static precise_euclidean_t precise_euclidean; + +/// \breif Checks if distance_type used exists. +template +struct check_distance_type +{ + static const bool value = std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value; +}; +} + +namespace mask_size { +struct three_t {}; +struct five_t {}; +struct not_applicable_t {}; + +static three_t three; +static five_t five; +static not_applicable_t not_applicable; + +/// \breif Checks if mask_size used exists. +template +struct check_mask_size +{ + static const bool value = std::is_same::value || + std::is_same::value || + std::is_same::value; +}; +} + +enum class distance_from +{ + off_pixels, + on_pixels +}; + +namespace detail { + +/// \breif Checks compatiblity of distance_type and mask_size used together. +template +struct dt_parameters_are_compatible +{ + static const bool value = true; +}; + +template +struct dt_parameters_are_compatible +{ + static const bool value = false; +}; + +template +struct dt_parameters_are_compatible +{ + static const bool value = false; +}; + +template <> +struct dt_parameters_are_compatible +{ + static const bool value = true; +}; + +// Value used as infinite distance for distance_transform. +float constexpr dt_infinite = 1000000000; + +/// \breif Calculates distance transfrom with mask size three. +/// Optimal local distances a, b for euclidean approximation from, +/// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf +template +void distance_transform_mask_size_three_impl(SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from, + DistanceType /*Passes type*/) +{ + int const padding = 1; + gray32f_image_t intermediate_image(src_view.width() + 2 * padding, + src_view.height() + 2 * padding); + gray32f_view_t intermediate_image_view = view(intermediate_image); + + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + { + intermediate_image_view(x, 0)[0] = dt_infinite ; + intermediate_image_view(x, intermediate_image_view.height() - 1)[0] = dt_infinite; + } + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + { + intermediate_image_view(0, y)[0] = dt_infinite ; + intermediate_image_view(intermediate_image_view.width() - 1, y)[0] = dt_infinite ; + } + + float constexpr src_channel_min = + (std::numeric_limits ::type>::min)(); + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if(dist_from == distance_from::off_pixels) + { + if (src_view(x, y)[0] == src_channel_min) + intermediate_image_view(x + padding, y + padding)[0] = 0; + else + intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; + } + else + { + if (src_view(x, y)[0] != src_channel_min) + intermediate_image_view(x + padding, y + padding)[0] = 0; + else + intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; + } + } + } + + float a, b; + + if(std::is_same::value) + { + a = 0.95509f; + b = 1.36930f; + } + else if(std::is_same::value) + { + a = 1; + b = 2; + } + else + { + a = 1; + b = 1; + } + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if(intermediate_image_view(x + padding, y + padding)[0] > 0) + { + float const d1 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; + float const d2 = b + intermediate_image_view(x - 1 + padding, y - 1 + padding)[0]; + float const d3 = a + intermediate_image_view(x + padding, y - 1 + padding)[0]; + float const d4 = b + intermediate_image_view(x + 1 + padding, y - 1 + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] = std::min({d1, d2, d3, d4}); + } + } + } + + for (std::ptrdiff_t y = src_view.height() - 1; y >= 0; --y) + { + for (std::ptrdiff_t x = src_view.width() - 1; x >= 0; --x) + { + if (intermediate_image_view(x + padding, y + padding)[0] > 0) + { + float const d1 = a + intermediate_image_view(x + 1 + padding, y + padding)[0]; + float const d2 = b + intermediate_image_view(x + 1 + padding, y + 1 + padding)[0]; + float const d3 = a + intermediate_image_view(x + padding, y + 1 + padding)[0]; + float const d4 = b + intermediate_image_view(x - 1 + padding, y + 1 + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] = std::min({ d1, d2, d3, d4, + static_cast(intermediate_image_view(x + padding, y + padding)[0]) }); + + float const distance_transform = intermediate_image_view + (x + padding, y + padding)[0]; + + float constexpr dst_channel_max = + (is_same::value) ? dt_infinite : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min = + (is_same::value) ? 0.f : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] = + static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max : + (distance_transform < dst_channel_min) ? dst_channel_min : + distance_transform); + } + } + } +} + +/// \breif Calculates distance transfrom with mask size five. +/// Optimal local distances a, b, c for euclidean approximation from, +/// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf +template +void distance_transform_mask_size_five_impl(SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from, + DistanceType /*Passes type*/) +{ + int const padding = 2; + gray32f_image_t intermediate_image(src_view.width() + 2 * padding, + src_view.height() + 2 * padding); + gray32f_view_t intermediate_image_view = view(intermediate_image); + + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + { + intermediate_image_view(x, 0)[0] = dt_infinite; + intermediate_image_view(x, 1)[0] = dt_infinite; + intermediate_image_view(x, intermediate_image_view.height() - 1)[0] = dt_infinite; + intermediate_image_view(x, intermediate_image_view.height() - 2)[0] = dt_infinite; + } + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + { + intermediate_image_view(0, y)[0] = dt_infinite ; + intermediate_image_view(1, y)[0] = dt_infinite ; + intermediate_image_view(intermediate_image_view.width() - 1, y)[0] = dt_infinite; + intermediate_image_view(intermediate_image_view.width() - 2, y)[0] = dt_infinite; + } + + float constexpr src_channel_min = + (std::numeric_limits::type>::min)(); + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if(dist_from == distance_from::off_pixels) + { + if (src_view(x, y)[0] == src_channel_min) + intermediate_image_view(x + padding, y + padding)[0] = 0; + else + intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; + } + else + { + if (src_view(x, y)[0] != src_channel_min) + intermediate_image_view(x + padding, y + padding)[0] = 0; + else + intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; + } + } + } + + float a, b, c; + + if(std::is_same::value) + { + a = 1; + b = 1.4f; + c = 2.19691f; + } + else if(std::is_same::value) + { + a = 1; + b = 2; + c = 3; + } + else + { + a = 1; + b = 1; + c = 2; + } + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if(intermediate_image_view(x + padding, y + padding)[0] > 0) + { + float const d1 = c + intermediate_image_view(x - 1 + padding, y - 2 + padding)[0]; + float const d2 = c + intermediate_image_view(x + 1 + padding, y - 2 + padding)[0]; + float const d3 = c + intermediate_image_view(x - 2 + padding, y - 1 + padding)[0]; + float const d4 = b + intermediate_image_view(x - 1 + padding, y - 1 + padding)[0]; + float const d5 = a + intermediate_image_view(x + padding, y - 1 + padding)[0]; + float const d6 = b + intermediate_image_view(x + 1 + padding, y - 1 + padding)[0]; + float const d7 = c + intermediate_image_view(x + 2 + padding, y - 1 + padding)[0]; + float const d8 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] = + std::min({d1, d2, d3, d4, d5, d6, d7, d8}); + } + } + } + + for (std::ptrdiff_t y = src_view.height() - 1; y >= 0; --y) + { + for (std::ptrdiff_t x = src_view.width() - 1; x >= 0; --x) + { + if (intermediate_image_view(x + padding, y + padding)[0] > 0) + { + float const d1 = c + intermediate_image_view(x + 1 + padding, y + 2 + padding)[0]; + float const d2 = c + intermediate_image_view(x - 1 + padding, y + 2 + padding)[0]; + float const d3 = c + intermediate_image_view(x + 2 + padding, y + 1 + padding)[0]; + float const d4 = b + intermediate_image_view(x + 1 + padding, y + 1 + padding)[0]; + float const d5 = a + intermediate_image_view(x + padding, y + 1 + padding)[0]; + float const d6 = b + intermediate_image_view(x - 1 + padding, y + 1 + padding)[0]; + float const d7 = c + intermediate_image_view(x - 2 + padding, y + 1 + padding)[0]; + float const d8 = a + intermediate_image_view(x + 1 + padding, y + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] = + std::min({ d1, d2, d3, d4, d5, d6, d7, d8, + static_cast(intermediate_image_view(x + padding, y + padding)[0]) }); + + float const distance_transform = + intermediate_image_view (x + padding, y + padding)[0]; + + float constexpr dst_channel_max = + (is_same::value) ? dt_infinite : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min = + (is_same::value) ? 0.f : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] = + static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max : + (distance_transform < dst_channel_min) ? dst_channel_min : + distance_transform); + } + } + } +} + +/// \breif Calculates one-dimensional squared euclidean distance. +/// +/// Reference - http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf +template +std::vector calculate_squared_euclidean_distance(ImageIterator f, std::ptrdiff_t n) +{ + std::ptrdiff_t k = 0; + std::vector d(n); + std::vector v(n); + std::vector z(n + 1); + v[0] = 0; + z[0] = static_cast(-dt_infinite); + z[1] = static_cast(dt_infinite); + for(int q = 1; q < n ; ++q) + { + float s = static_cast(((f[q][0] + std::pow(q, 2)) - + (f[v[k]][0] + std::pow(v[k], 2))) / (2 * q - 2 * v[k])); + while (s <= z[k]) + { + --k; + s = static_cast(((f[q][0] + std::pow(q, 2)) - + (f[v[k]][0] + std::pow(v[k], 2))) / (2 * q - 2 * v[k])); + } + { + k = k + 1; + v[k] = static_cast(q); + z[k] = static_cast(s); + z[k + 1] = static_cast(dt_infinite); + } + } + k = 0; + for (int q = 0; q < n; ++q) + { + while(z[k+1] < q) + { + ++k; + } + d[q] = static_cast(std::pow((q - v[k]), 2) + f[v[k]][0]); + } + return d; +} + +/// \breif Calculates precise euclidean distance transform. +template +void distance_transorm_precise_impl(SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from) +{ + gray32f_image_t intermediate_image(src_view.dimensions()); + gray32f_view_t intermediate_image_view = view(intermediate_image); + float constexpr src_channel_min = + (std::numeric_limits::type>::min)(); + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + if (dist_from == distance_from::off_pixels) + { + if (src_view(x, y)[0] == src_channel_min) intermediate_image_view(x, y)[0] = 0; + else intermediate_image_view(x, y)[0] = dt_infinite; + } + else + { + if (src_view(x, y)[0] != src_channel_min) intermediate_image_view(x, y)[0] = 0; + else intermediate_image_view(x, y)[0] = dt_infinite; + } + } + } + + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + { + typename gray32f_view_t::x_iterator intermediate_row_itr = + intermediate_image_view.row_begin(y); + + std::vector one_dimensional_squared_distance = calculate_squared_euclidean_distance + (intermediate_row_itr, intermediate_image_view.width()); + + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + { + intermediate_row_itr[x][0] = one_dimensional_squared_distance[x]; + } + } + + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + { + typename gray32f_view_t::y_iterator intermediate_col_itr = + intermediate_image_view.col_begin(x); + + std::vector one_dimensional_squared_distance = calculate_squared_euclidean_distance + (intermediate_col_itr, intermediate_image_view.height()); + + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + { + intermediate_col_itr[y][0] = one_dimensional_squared_distance[y]; + + float const distance_transform = std::sqrt(intermediate_col_itr[y][0]); + + float constexpr dst_channel_max = + (is_same::value) ? dt_infinite : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min = + (is_same::value) ? 0.f : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] = + static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max : + (distance_transform < dst_channel_min) ? dst_channel_min : + distance_transform); + } + } +} + +} //namespace detail + +/// \addtogroup ImageProcessing +/// @{ +/// \breif Calculates distance to the nearest 'on' OR 'off' pixel (as specified), +/// for each pixel of a binary image. +/// +/// \param src_view - Source image view. +/// \param dst_view - Destination image view. +/// \param dist_from - Specifies from where to find distance from, neareast, 'on' OR 'off' pixels. +/// \param dist_type - Specifies formula/method for distance calculation. +/// \tparam SrcView - Source image view type. +/// \tparam DstView - Destination image view type. +/// \tparam DistanceType - Distance type choosen. +/// \tparam MaskSize - Mask size choosen. +template +void distance_transform(SrcView const& src_view, DstView const& dst_view, distance_from dist_from, + DistanceType dist_type, MaskSize /*Passes type*/) +{ + gil_function_requires>(); + gil_function_requires>(); + + static_assert(is_same::type>::value && + is_same::type>::value, + "Source and destination image views must use gray color space."); + + static_assert(distance_type::check_distance_type::value, + "Distance type not recognized."); + static_assert(mask_size::check_mask_size::value, "Mask Size not recognized."); + + // Generates compile time error if invalid combination of distance_type and mask_size is used. + static_assert(detail::dt_parameters_are_compatible::value, + "distance_transform is incompatible with combination of " + "distance_type and mask_size used."); + + BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); + + if(std::is_same::value) + distance_transform_mask_size_three_impl(src_view, dst_view, dist_from, dist_type); + + else if (std::is_same::value) + distance_transform_mask_size_five_impl(src_view, dst_view, dist_from, dist_type); + + else + distance_transorm_precise_impl(src_view, dst_view, dist_from); +} +/// @} +}} // namespace boost::gil +#endif // BOOST_GIL_IMAGE_PROCESSING_DISTANCE_TRANSFORM_HPP diff --git a/test/core/image_processing/CMakeLists.txt b/test/core/image_processing/CMakeLists.txt index 65fa032fd9..b7991a29a4 100644 --- a/test/core/image_processing/CMakeLists.txt +++ b/test/core/image_processing/CMakeLists.txt @@ -25,7 +25,8 @@ foreach(_name convolve_rows kernel kernel_fixed - sharpening) + sharpening + distance_transform) set(_test t_core_image_processing_${_name}) set(_target test_core_image_processing_${_name}) diff --git a/test/core/image_processing/Jamfile b/test/core/image_processing/Jamfile index 7bf11a87d3..e4982773bb 100644 --- a/test/core/image_processing/Jamfile +++ b/test/core/image_processing/Jamfile @@ -29,3 +29,4 @@ run convolve_rows.cpp ; run kernel.cpp ; run kernel_fixed.cpp ; run sharpening.cpp ; +run distance_transform.cpp ; diff --git a/test/core/image_processing/distance_transform.cpp b/test/core/image_processing/distance_transform.cpp new file mode 100644 index 0000000000..e36a9cb185 --- /dev/null +++ b/test/core/image_processing/distance_transform.cpp @@ -0,0 +1,480 @@ +// +// Copyright 2021 Harsit Pant +// +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#include +#include +#include +#include + +namespace gil = boost::gil; + +template +void fill_image_view(std::vector >& values, ImgView image_view) +{ + for (std::ptrdiff_t y = 0; y < image_view.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < image_view.width(); ++x) + { + image_view(x, y)[0] = values[y][x]; + } + } +} + +template +void check_for_negligible_varition(ImgView1 const& image_view1, ImgView2 const& image_view2) +{ + for (std::ptrdiff_t y = 0; y < image_view1.height(); ++y) + { + for (std::ptrdiff_t x = 0; x < image_view1.width(); ++x) + { + float variation = std::abs(image_view1(x, y)[0] - image_view2(x, y)[0]); + BOOST_TEST_LT(variation, 0.1); + } + } +} + +void test_manhattan_uint8_t_input_uint8_t_output_distance_from_off_pixels() +{ + + gil::gray8_image_t img_in(7, 7); + gil::gray8_image_t img_expected(7, 7); + gil::gray8_image_t img_out(7, 7); + + //test values from wikipedia, in 8 bit binary form + std::vector> val_in + { + {0, 0, 0, 0, 0, 0,0}, + {0,255,255,255,255,255,0}, + {0,255,255,255,255,255,0}, + {0,255,255,255,255,255,0}, + {0,255,255,255,255,255,0}, + {0,255,255,255,255,255,0}, + {0, 0, 0, 0, 0, 0,0} + }; + + std::vector> val_exp + { + {0,0,0,0,0,0,0}, + {0,1,1,1,1,1,0}, + {0,1,2,2,2,1,0}, + {0,1,2,3,2,1,0}, + {0,1,2,2,2,1,0}, + {0,1,1,1,1,1,0}, + {0,0,0,0,0,0,0} + }; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp, view(img_expected)); + + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::manhattan, gil::mask_size::three); + + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); +} + + +void test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels() +{ + gil::gray16_image_t img_in(7, 7); + gil::gray8_image_t img_expected(7, 7); + gil::gray8_image_t img_out(7, 7); + + std::vector> val_in + { + {0, 65535, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 65535, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} + }; + + std::vector> val_exp + { + {1, 0, 1, 2, 3, 4, 4}, + {1, 1, 1, 2, 3, 3, 3}, + {2, 2, 2, 2, 2, 2, 3}, + {3, 2, 1, 1, 1, 2, 3}, + {3, 2, 1, 0, 1, 2, 3}, + {3, 2, 1, 1, 1, 2, 3}, + {3, 2, 2, 2, 2, 2, 3} + }; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp, view(img_expected)); + + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::on_pixels, + gil::distance_type::chessboard, gil::mask_size::three); + + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); +} + +void test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance_from_off_pixels() +{ + gil::gray8_image_t img_in(15, 15); + gil::gray32f_image_t img_expected(15, 15); + gil::gray32f_image_t img_out(15, 15); + + std::vector> val_in + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} + }; + + std::vector> val_exp1 + { + {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f}, + { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + { 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + { 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + { 1, 0, 1, 2, 1.4f, 1, 1, 1, 1, 1, 1.4f, 2, 1, 0, 1}, + { 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + { 1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + { 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1}, + { 1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + { 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + { 1, 0, 1, 2, 1.4f, 1, 1, 1, 1, 1, 1.4f, 2, 1, 0, 1}, + { 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + { 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f} + }; + + std::vector> val_exp2 + { + {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + {1, 0, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 0, 1}, + {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2} + }; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp1, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, gil::mask_size::five); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + fill_image_view(val_exp2, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::manhattan, gil::mask_size::five); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + +} + +void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() +{ + gil::gray8_image_t img_in(15, 15); + gil::gray32f_image_t img_expected(15, 15); + gil::gray32f_image_t img_out(15, 15); + + std::vector> val_in + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} , + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0} + }; + + std::vector> val_exp_precise + { + {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 4.1231055f, 3.1622777f, + 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, + { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4, + 3, 2, 1, 0, 1, 2, 3, 4}, + { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 3.1622777f, + 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, + { 2, 1, 0, 1, 2, 3, 4, 3.6055512f, + 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f}, + { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 4.2426405f, + 3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5}, + { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f, 5, + 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f}, + {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5, 5.8309517f, + 5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f}, + { 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f, 6.4031243f, + 6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.0000005f}, + {5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f, 7.0710683f, + 7.2801094f, 7.0710683f, 7.0000005f, 6.7082043f, 6.3245554f, 6.0827622f, 6}, + {6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.2111025f, 7.8102503f, + 7.8102503f, 7.0710683f, 6.4031243f, 5.8309517f, 5.3851647f, 5.0990195f, 5}, + {7.2801094f, 7.0710683f, 7.0000005f, 7.0710683f, 7.2801094f, 7.6157737f, 8.062258f, 8.062258f, + 7.2111025f, 6.4031243f, 5.656854f, 5, 4.472136f, 4.1231055f, 4}, + { 8.246211f, 8.062258f, 8, 8.062258f, 8.246211f, 8.5440035f, 8.5440035f, 7.6157737f, + 6.7082043f, 5.8309517f, 5, 4.2426405f, 3.6055512f, 3.1622777f, 3}, + { 9.219544f, 9.055386f, 9, 9.055386f, 9.219544f, 9.219544f, 8.246211f, 7.2801094f, + 6.3245554f, 5.3851647f, 4.472136f, 3.6055512f, 2.828427f, 2.236068f, 2}, + {10.198039f, 10.049875f, 10, 10.049875f, 10.049875f, 9.055386f, 8.062258f, 7.0710683f, + 6.0827622f, 5.0990195f, 4.1231055f, 3.1622777f, 2.236068f, 1.4142135f, 1}, + {11.180341f, 11.045361f, 11, 11, 10, 9, 8, 7, + 6, 5, 4, 3, 2, 1, 0} + }; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp_precise, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + + check_for_negligible_varition(view(img_out), view(img_expected)); + + std::vector> val_exp_euclidean_three + { + { 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, 3.6935883f, 4.1078796f, + 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, + 2.324295f, 3.2792969f, 4.2342987f}, + { 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, 2.7385864f, 3.6935883f, + 3.8200073f, 2.8650055f, 1.9100037f, 0.95500183f, 0, 0.95500183f, + 1.9100037f, 2.8650055f, 3.8200073f}, + { 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, + 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, + 2.324295f, 3.2792969f, 4.2342987f}, + { 1.9100037f, 0.95500183f, 0, 0.95500183f, 1.9100037f, 2.8650055f, + 3.8200073f, 3.6935883f, 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, + 2.7385864f, 3.6935883f, 4.64859f}, + { 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, + 4.2342987f, 4.1078796f, 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, + 3.6935883f, 4.1078796f, 5.0628815f}, + { 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, 2.7385864f, 3.6935883f, + 4.64859f, 5.0628815f, 4.64859f, 4.2342987f, 3.8200073f, 4.2342987f, + 4.64859f, 5.0628815f, 5.477173f}, + { 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, 3.6935883f, 4.1078796f, + 5.0628815f, 6.0178833f, 5.603592f, 5.1893005f, 4.775009f, 5.1893005f, + 5.603592f, 6.0178833f, 6.4321747f}, + { 4.64859f, 4.2342987f, 3.8200073f, 4.2342987f, 4.64859f, 5.0628815f, + 5.477173f, 6.4321747f, 6.5585938f, 6.1443024f, 5.730011f, 6.1443024f, + 6.5585938f, 6.972885f, 6.685013f}, + { 5.603592f, 5.1893005f, 4.775009f, 5.1893005f, 5.603592f, 6.0178833f, + 6.4321747f, 6.846466f, 7.5135956f, 7.099304f, 6.685013f, 6.972885f, + 6.5585938f, 6.1443024f, 5.730011f}, + { 6.5585938f, 6.1443024f, 5.730011f, 6.1443024f, 6.5585938f, 6.972885f, + 7.3871765f, 7.801468f, 7.801468f, 6.846466f, 6.4321747f, 6.0178833f, + 5.603592f, 5.1893005f, 4.775009f}, + { 7.5135956f, 7.099304f, 6.685013f, 7.099304f, 7.5135956f, 7.927887f, + 8.342178f, 8.342178f, 7.3871765f, 6.4321747f, 5.477173f, 5.0628815f, + 4.64859f, 4.2342987f, 3.8200073f}, + { 8.468597f, 8.054306f, 7.6400146f, 8.054306f, 8.468597f, 8.882889f, + 8.882889f, 7.927887f, 6.972885f, 6.0178833f, 5.0628815f, 4.1078796f, + 3.6935883f, 3.2792969f, 2.8650055f}, + { 9.423599f, 9.009308f, 8.5950165f, 9.009308f, 9.423599f, 9.423599f, + 8.468597f, 7.5135956f, 6.5585938f, 5.603592f, 4.64859f, 3.6935883f, + 2.7385864f, 2.324295f, 1.9100037f}, + { 10.378601f, 9.96431f, 9.550018f, 9.96431f, 9.96431f, 9.009308f, + 8.054306f, 7.099304f, 6.1443024f, 5.1893005f, 4.2342987f, 3.2792969f, + 2.324295f, 1.3692932f, 0.95500183f}, + { 11.333603f, 10.919312f, 10.50502f, 10.50502f, 9.550018f, 8.5950165f, + 7.6400146f, 6.685013f, 5.730011f, 4.775009f, 3.8200073f, 2.8650055f, + 1.9100037f, 0.95500183f, 0.f} + }; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp_euclidean_three, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, gil::mask_size::three); + + check_for_negligible_varition(view(img_out), view(img_expected)); + + std::vector> val_exp_euclidean_five + { + {3.5969f, 3.1969f, 3, 3.1969f, 3.5969f, 4.2f, + 4.1969f, 3.1969f, 2.1969f, 1.4f, 1, 1.4f, + 2.1969f, 3.1969f, 4.1969f}, + { 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, + 4, 3, 2, 1, 0, 1, + 2, 3, 4}, + {2.1969f, 1.4f, 1, 1.4f, 2.1969f, 3.1969f, + 4.1969f, 3.1969f, 2.1969f, 1.4f, 1, 1.4f, + 2.1969f, 3.1969f, 4.1969f}, + { 2, 1, 0, 1, 2, 3, + 4, 3.5969f, 2.8f, 2.1969f, 2, 2.1969f, + 2.8f, 3.5969f, 4.3938f}, + {2.1969f, 1.4f, 1, 1.4f, 2.1969f, 3.1969f, + 4.1969f, 4.2f, 3.5969f, 3.1969f, 3, 3.1969f, + 3.5969f, 4.2f, 4.9968996f}, + { 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, + 4.3938f, 4.9968996f, 4.3938f, 4.1969f, 4, 4.1969f, + 4.3938f, 4.9968996f, 5.6f}, + {3.5969f, 3.1969f, 3, 3.1969f, 3.5969f, 4.2f, + 4.9968996f, 5.7938f, 5.3938f, 5.1969f, 5, 5.1969f, + 5.3938f, 5.7938f, 6.3968997f}, + {4.3938f, 4.1969f, 4, 4.1969f, 4.3938f, 4.9968996f, + 5.6f, 6.3968997f, 6.3938f, 6.1969f, 6, 6.1969f, + 6.3938f, 6.5906997f, 7}, + {5.3938f, 5.1969f, 5, 5.1969f, 5.3938f, 5.7938f, + 6.3968997f, 7, 7.3938f, 7.1969f, 7, 6.5906997f, + 6.3938f, 6.1969f, 6}, + {6.3938f, 6.1969f, 6, 6.1969f, 6.3938f, 6.5906997f, + 7.1937995f, 7.7969f, 7.7969f, 7, 6.3968997f, 5.7938f, + 5.3938f, 5.1969f, 5}, + {7.3938f, 7.1969f, 7, 7.1969f, 7.3938f, 7.5906997f, + 7.9907f, 7.9907f, 7.1937995f, 6.3968997f, 5.6f, 4.9968996f, + 4.3938f, 4.1969f, 4}, + {8.3938f, 8.196899f, 8, 8.196899f, 8.3938f, 8.5907f, + 8.5907f, 7.5906997f, 6.5906997f, 5.7938f, 4.9968996f, 4.2f, + 3.5969f, 3.1969f, 3}, + {9.3938f, 9.196899f, 9, 9.196899f, 9.3938f, 9.3938f, + 8.3938f, 7.3938f, 6.3938f, 5.3938f, 4.3938f, 3.5969f, + 2.8f, 2.1969f, 2}, + {10.393799f, 10.196899f, 10, 10.196899f, 10.196899f, 9.196899f, + 8.196899f, 7.1969f, 6.1969f, 5.1969f, 4.1969f, 3.1969f, + 2.1969f, 1.4f, 1}, + {11.393799f, 11.196899f, 11, 11, 10, 9, + 8, 7, 6, 5, 4, 3, + 2, 1, 0} + }; + + fill_image_view(val_in, view(img_in)); + fill_image_view(val_exp_euclidean_five, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, gil::mask_size::five); + + check_for_negligible_varition(view(img_out), view(img_expected)); + + + std::vector> val_exp_manhatten_three + { + {5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, + {3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, + {4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, + {5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, + {6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, + {7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, + {8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, + {9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, + {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, + {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, + {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, + {13, 12, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + }; + + fill_image_view(val_exp_manhatten_three, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::manhattan, gil::mask_size::three); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + + + std::vector> val_exp_manhatten_five + { + {5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, + {3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, + {4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, + {5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, + {6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, + {7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, + {8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, + {9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, + {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, + {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, + {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, + {13, 12, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + }; + + fill_image_view(val_exp_manhatten_five, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::manhattan, gil::mask_size::five); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + + std::vector> val_exp_chessboard_three + { + {3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + {3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, + {4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, + {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, + {6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, + {7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, + {8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, + {9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, + {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, + {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + }; + + fill_image_view(val_exp_chessboard_three, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::chessboard, gil::mask_size::three); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + std::vector> val_exp_chessboard_five + { + {3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + {3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, + {4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, + {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, + {6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, + {7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, + {8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, + {9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, + {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, + {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + }; + + fill_image_view(val_exp_chessboard_five, view(img_expected)); + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, + gil::distance_type::chessboard, gil::mask_size::five); + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + +} + +int main() +{ + test_manhattan_uint8_t_input_uint8_t_output_distance_from_off_pixels(); + test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels(); + test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance_from_off_pixels(); + test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels(); + return ::boost::report_errors(); +} From 1aeb7a6af94f4aceb4cd537c0931b3eb377acad1 Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Sun, 4 Apr 2021 00:36:24 +0530 Subject: [PATCH 11/37] Improve test coverage --- .../image_processing/distance_transform.cpp | 208 +++++++++++------- 1 file changed, 123 insertions(+), 85 deletions(-) diff --git a/test/core/image_processing/distance_transform.cpp b/test/core/image_processing/distance_transform.cpp index e36a9cb185..ebafdf35c8 100644 --- a/test/core/image_processing/distance_transform.cpp +++ b/test/core/image_processing/distance_transform.cpp @@ -82,6 +82,8 @@ void test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels() gil::gray16_image_t img_in(7, 7); gil::gray8_image_t img_expected(7, 7); gil::gray8_image_t img_out(7, 7); + gil::gray32f_image_t img_expected_float32_t(7, 7); + gil::gray32f_image_t img_out_float32_t(7, 7); std::vector> val_in { @@ -94,7 +96,7 @@ void test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels() {0, 0, 0, 0, 0, 0, 0} }; - std::vector> val_exp + std::vector> val_exp_chessboard_three { {1, 0, 1, 2, 3, 4, 4}, {1, 1, 1, 2, 3, 3, 3}, @@ -106,12 +108,48 @@ void test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels() }; fill_image_view(val_in, view(img_in)); - fill_image_view(val_exp, view(img_expected)); + fill_image_view(val_exp_chessboard_three, view(img_expected)); gil::distance_transform(view(img_in), view(img_out), gil::distance_from::on_pixels, gil::distance_type::chessboard, gil::mask_size::three); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + std::vector> val_exp_manhattan_five + { + {1, 0, 1, 2, 3, 4, 5}, + {2, 1, 2, 3, 4, 5, 6}, + {3, 2, 3, 2, 3, 4, 5}, + {4, 3, 2, 1, 2, 3, 4}, + {3, 2, 1, 0, 1, 2, 3}, + {4, 3, 2, 1, 2, 3, 4}, + {5, 4, 3, 2, 3, 4, 5} + }; + + fill_image_view(val_exp_manhattan_five, view(img_expected)); + + gil::distance_transform(view(img_in), view(img_out), gil::distance_from::on_pixels, + gil::distance_type::manhattan, gil::mask_size::five); + + BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); + + std::vector> val_exp_euclidean_precise + { + { 1, 0, 1, 2, 3, 4, 5}, + {1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 3.6055512f, 4.2426405f}, + { 2.236068f, 2, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f}, + {3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f}, + { 3, 2, 1, 0, 1, 2, 3}, + {3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f}, + {3.6055512f, 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f} + }; + + fill_image_view(val_exp_euclidean_precise, view(img_expected_float32_t)); + + gil::distance_transform(view(img_in), view(img_out_float32_t), gil::distance_from::on_pixels, + gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + + BOOST_TEST(equal_pixels(const_view(img_out_float32_t), const_view(img_expected_float32_t))); } void test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance_from_off_pixels() @@ -217,36 +255,36 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() std::vector> val_exp_precise { - {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 4.1231055f, 3.1622777f, - 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, - { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4, - 3, 2, 1, 0, 1, 2, 3, 4}, - { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 3.1622777f, - 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, - { 2, 1, 0, 1, 2, 3, 4, 3.6055512f, - 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f}, - { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 4.2426405f, - 3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5}, - { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f, 5, - 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f}, - {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5, 5.8309517f, - 5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f}, - { 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f, 6.4031243f, - 6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.0000005f}, - {5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f, 7.0710683f, - 7.2801094f, 7.0710683f, 7.0000005f, 6.7082043f, 6.3245554f, 6.0827622f, 6}, - {6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.2111025f, 7.8102503f, - 7.8102503f, 7.0710683f, 6.4031243f, 5.8309517f, 5.3851647f, 5.0990195f, 5}, - {7.2801094f, 7.0710683f, 7.0000005f, 7.0710683f, 7.2801094f, 7.6157737f, 8.062258f, 8.062258f, - 7.2111025f, 6.4031243f, 5.656854f, 5, 4.472136f, 4.1231055f, 4}, - { 8.246211f, 8.062258f, 8, 8.062258f, 8.246211f, 8.5440035f, 8.5440035f, 7.6157737f, - 6.7082043f, 5.8309517f, 5, 4.2426405f, 3.6055512f, 3.1622777f, 3}, - { 9.219544f, 9.055386f, 9, 9.055386f, 9.219544f, 9.219544f, 8.246211f, 7.2801094f, - 6.3245554f, 5.3851647f, 4.472136f, 3.6055512f, 2.828427f, 2.236068f, 2}, - {10.198039f, 10.049875f, 10, 10.049875f, 10.049875f, 9.055386f, 8.062258f, 7.0710683f, - 6.0827622f, 5.0990195f, 4.1231055f, 3.1622777f, 2.236068f, 1.4142135f, 1}, - {11.180341f, 11.045361f, 11, 11, 10, 9, 8, 7, - 6, 5, 4, 3, 2, 1, 0} + {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 4.1231055f, 3.1622777f, + 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, + { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4, + 3, 2, 1, 0, 1, 2, 3, 4}, + { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 3.1622777f, + 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, + { 2, 1, 0, 1, 2, 3, 4, 3.6055512f, + 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f}, + { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 4.2426405f, + 3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5}, + { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f, 5, + 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f}, + {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5, 5.8309517f, + 5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f}, + { 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f, 6.4031243f, + 6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.0000005f}, + {5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f, 7.0710683f, + 7.2801094f, 7.0710683f, 7.0000005f, 6.7082043f, 6.3245554f, 6.0827622f, 6}, + {6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.2111025f, 7.8102503f, + 7.8102503f, 7.0710683f, 6.4031243f, 5.8309517f, 5.3851647f, 5.0990195f, 5}, + {7.2801094f, 7.0710683f, 7.0000005f, 7.0710683f, 7.2801094f, 7.6157737f, 8.062258f, 8.062258f, + 7.2111025f, 6.4031243f, 5.656854f, 5, 4.472136f, 4.1231055f, 4}, + { 8.246211f, 8.062258f, 8, 8.062258f, 8.246211f, 8.5440035f, 8.5440035f, 7.6157737f, + 6.7082043f, 5.8309517f, 5, 4.2426405f, 3.6055512f, 3.1622777f, 3}, + { 9.219544f, 9.055386f, 9, 9.055386f, 9.219544f, 9.219544f, 8.246211f, 7.2801094f, + 6.3245554f, 5.3851647f, 4.472136f, 3.6055512f, 2.828427f, 2.236068f, 2}, + {10.198039f, 10.049875f, 10, 10.049875f, 10.049875f, 9.055386f, 8.062258f, 7.0710683f, + 6.0827622f, 5.0990195f, 4.1231055f, 3.1622777f, 2.236068f, 1.4142135f, 1}, + {11.180341f, 11.045361f, 11, 11, 10, 9, 8, 7, + 6, 5, 4, 3, 2, 1, 0} }; fill_image_view(val_in, view(img_in)); @@ -258,27 +296,27 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() std::vector> val_exp_euclidean_three { - { 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, 3.6935883f, 4.1078796f, - 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, + { 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, 3.6935883f, 4.1078796f, + 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, 4.2342987f}, - { 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, 2.7385864f, 3.6935883f, - 3.8200073f, 2.8650055f, 1.9100037f, 0.95500183f, 0, 0.95500183f, + { 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, 2.7385864f, 3.6935883f, + 3.8200073f, 2.8650055f, 1.9100037f, 0.95500183f, 0, 0.95500183f, 1.9100037f, 2.8650055f, 3.8200073f}, - { 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, - 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, + { 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, + 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, 4.2342987f}, - { 1.9100037f, 0.95500183f, 0, 0.95500183f, 1.9100037f, 2.8650055f, - 3.8200073f, 3.6935883f, 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, - 2.7385864f, 3.6935883f, 4.64859f}, - { 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, - 4.2342987f, 4.1078796f, 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, + { 1.9100037f, 0.95500183f, 0, 0.95500183f, 1.9100037f, 2.8650055f, + 3.8200073f, 3.6935883f, 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, + 2.7385864f, 3.6935883f, 4.64859f}, + { 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, + 4.2342987f, 4.1078796f, 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, 3.6935883f, 4.1078796f, 5.0628815f}, - { 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, 2.7385864f, 3.6935883f, - 4.64859f, 5.0628815f, 4.64859f, 4.2342987f, 3.8200073f, 4.2342987f, + { 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, 2.7385864f, 3.6935883f, + 4.64859f, 5.0628815f, 4.64859f, 4.2342987f, 3.8200073f, 4.2342987f, 4.64859f, 5.0628815f, 5.477173f}, { 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, 3.6935883f, 4.1078796f, 5.0628815f, 6.0178833f, 5.603592f, 5.1893005f, 4.775009f, 5.1893005f, - 5.603592f, 6.0178833f, 6.4321747f}, + 5.603592f, 6.0178833f, 6.4321747f}, { 4.64859f, 4.2342987f, 3.8200073f, 4.2342987f, 4.64859f, 5.0628815f, 5.477173f, 6.4321747f, 6.5585938f, 6.1443024f, 5.730011f, 6.1443024f, 6.5585938f, 6.972885f, 6.685013f}, @@ -295,13 +333,13 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() 8.882889f, 7.927887f, 6.972885f, 6.0178833f, 5.0628815f, 4.1078796f, 3.6935883f, 3.2792969f, 2.8650055f}, { 9.423599f, 9.009308f, 8.5950165f, 9.009308f, 9.423599f, 9.423599f, - 8.468597f, 7.5135956f, 6.5585938f, 5.603592f, 4.64859f, 3.6935883f, + 8.468597f, 7.5135956f, 6.5585938f, 5.603592f, 4.64859f, 3.6935883f, 2.7385864f, 2.324295f, 1.9100037f}, - { 10.378601f, 9.96431f, 9.550018f, 9.96431f, 9.96431f, 9.009308f, - 8.054306f, 7.099304f, 6.1443024f, 5.1893005f, 4.2342987f, 3.2792969f, + { 10.378601f, 9.96431f, 9.550018f, 9.96431f, 9.96431f, 9.009308f, + 8.054306f, 7.099304f, 6.1443024f, 5.1893005f, 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f}, { 11.333603f, 10.919312f, 10.50502f, 10.50502f, 9.550018f, 8.5950165f, - 7.6400146f, 6.685013f, 5.730011f, 4.775009f, 3.8200073f, 2.8650055f, + 7.6400146f, 6.685013f, 5.730011f, 4.775009f, 3.8200073f, 2.8650055f, 1.9100037f, 0.95500183f, 0.f} }; @@ -371,17 +409,17 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() std::vector> val_exp_manhatten_three { - {5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, - {4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, - {3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, - {2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, - {3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, - {4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, - {5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, - {6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, - {7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, - {8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, - {9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, + { 5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + { 4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + { 3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + { 2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, + { 3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, + { 4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, + { 5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, + { 6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, + { 7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, + { 8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, + { 9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, @@ -397,17 +435,17 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() std::vector> val_exp_manhatten_five { - {5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, - {4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, - {3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, - {2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, - {3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, - {4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, - {5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, - {6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, - {7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, - {8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, - {9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, + { 5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + { 4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + { 3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + { 2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, + { 3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, + { 4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, + { 5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, + { 6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, + { 7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, + { 8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, + { 9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, @@ -422,19 +460,19 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() std::vector> val_exp_chessboard_three { - {3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, - {2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, - {2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, - {2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, - {2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, - {2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, - {3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, - {4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, - {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, - {6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, - {7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, - {8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, - {9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, + { 3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + { 2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + { 2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + { 2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, + { 2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, + { 2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, + { 4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, + { 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, + { 7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, + { 8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, + { 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} }; From 147a2ccaed71d9b2f17153ab877871aae07cd4fd Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Thu, 10 Jun 2021 13:46:53 +0530 Subject: [PATCH 12/37] Improve formatting; Correct name in copyright info --- .../gil/image_processing/distance_transform.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/boost/gil/image_processing/distance_transform.hpp b/include/boost/gil/image_processing/distance_transform.hpp index 188ead88f1..8d7fc536b0 100644 --- a/include/boost/gil/image_processing/distance_transform.hpp +++ b/include/boost/gil/image_processing/distance_transform.hpp @@ -1,5 +1,5 @@ // -// Copyright 2021 Harsit Pant +// Copyright 2021 Harshit Pant // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -21,7 +21,8 @@ namespace boost { namespace gil { -namespace distance_type{ +namespace distance_type { + struct euclidean_approximation_t {}; struct manhattan_t {}; struct chessboard_t {}; @@ -41,9 +42,10 @@ struct check_distance_type std::is_same::value || std::is_same::value; }; -} +} // namespace distance_type namespace mask_size { + struct three_t {}; struct five_t {}; struct not_applicable_t {}; @@ -60,7 +62,7 @@ struct check_mask_size std::is_same::value || std::is_same::value; }; -} +} // namespace mask_size enum class distance_from { @@ -95,7 +97,7 @@ struct dt_parameters_are_compatible std::vector calculate_squared_euclidean_distance(ImageIterator f, std::ptrdiff_t n) From cc9ba61adb67e61866cc90c22835186af5430055 Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Thu, 10 Jun 2021 19:38:44 +0530 Subject: [PATCH 13/37] Improve Metafunctions Use constexpr wherever preferred; Replace static const bool by inheriting std::true_type or std::false_type in metafunctions. --- .../image_processing/distance_transform.hpp | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/include/boost/gil/image_processing/distance_transform.hpp b/include/boost/gil/image_processing/distance_transform.hpp index 8d7fc536b0..4eef99edf9 100644 --- a/include/boost/gil/image_processing/distance_transform.hpp +++ b/include/boost/gil/image_processing/distance_transform.hpp @@ -37,10 +37,10 @@ static precise_euclidean_t precise_euclidean; template struct check_distance_type { - static const bool value = std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value; + static constexpr bool value = std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value; }; } // namespace distance_type @@ -58,9 +58,9 @@ static not_applicable_t not_applicable; template struct check_mask_size { - static const bool value = std::is_same::value || - std::is_same::value || - std::is_same::value; + static constexpr bool value = std::is_same::value || + std::is_same::value || + std::is_same::value; }; } // namespace mask_size @@ -74,28 +74,19 @@ namespace detail { /// \breif Checks compatiblity of distance_type and mask_size used together. template -struct dt_parameters_are_compatible -{ - static const bool value = true; -}; +struct dt_parameters_are_compatible : std::true_type {}; template -struct dt_parameters_are_compatible -{ - static const bool value = false; -}; +struct dt_parameters_are_compatible + : std::false_type {}; template -struct dt_parameters_are_compatible -{ - static const bool value = false; -}; +struct dt_parameters_are_compatible + : std::false_type {}; template <> -struct dt_parameters_are_compatible -{ - static const bool value = true; -}; +struct dt_parameters_are_compatible + : std::true_type {}; /// \breif Value used as infinite distance for distance_transform. float constexpr dt_infinite = 1000000000; From 17c0649f652ae06b2f677b5593c7720b3f153b36 Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Fri, 11 Jun 2021 20:52:10 +0530 Subject: [PATCH 14/37] Move all metafunctions to detail namespace Moves 2 metafunctions: check_mask_size and check_distance_type to detail namespace and prefixes them with 'dt' (distance_transform) to avoid naming conflicts. --- .../image_processing/distance_transform.hpp | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/include/boost/gil/image_processing/distance_transform.hpp b/include/boost/gil/image_processing/distance_transform.hpp index 4eef99edf9..1b81641b3a 100644 --- a/include/boost/gil/image_processing/distance_transform.hpp +++ b/include/boost/gil/image_processing/distance_transform.hpp @@ -22,7 +22,6 @@ namespace boost { namespace gil { namespace distance_type { - struct euclidean_approximation_t {}; struct manhattan_t {}; struct chessboard_t {}; @@ -32,20 +31,9 @@ static euclidean_approximation_t euclidean_approximation; static manhattan_t manhattan; static chessboard_t chessboard; static precise_euclidean_t precise_euclidean; - -/// \breif Checks if distance_type used exists. -template -struct check_distance_type -{ - static constexpr bool value = std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value; -}; } // namespace distance_type namespace mask_size { - struct three_t {}; struct five_t {}; struct not_applicable_t {}; @@ -53,15 +41,6 @@ struct not_applicable_t {}; static three_t three; static five_t five; static not_applicable_t not_applicable; - -/// \breif Checks if mask_size used exists. -template -struct check_mask_size -{ - static constexpr bool value = std::is_same::value || - std::is_same::value || - std::is_same::value; -}; } // namespace mask_size enum class distance_from @@ -72,6 +51,26 @@ enum class distance_from namespace detail { +/// \breif Checks if mask_size used exists. +template +struct dt_check_mask_size +{ + static constexpr bool value = std::is_same::value || + std::is_same::value || + std::is_same::value; +}; + +/// \breif Checks if distance_type used exists. +template +struct dt_check_distance_type +{ + static constexpr bool value = + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value; +}; + /// \breif Checks compatiblity of distance_type and mask_size used together. template struct dt_parameters_are_compatible : std::true_type {}; @@ -458,7 +457,7 @@ void distance_transorm_precise_impl(SrcView const& src_view, } } -} //namespace detail +} // namespace detail /// \addtogroup ImageProcessing /// @{ @@ -480,13 +479,13 @@ void distance_transform(SrcView const& src_view, DstView const& dst_view, distan gil_function_requires>(); gil_function_requires>(); - static_assert(is_same::type>::value && - is_same::type>::value, + static_assert(std::is_same::type>::value && + std::is_same::type>::value, "Source and destination image views must use gray color space."); - static_assert(distance_type::check_distance_type::value, + static_assert(detail::dt_check_distance_type::value, "Distance type not recognized."); - static_assert(mask_size::check_mask_size::value, "Mask Size not recognized."); + static_assert(detail::dt_check_mask_size::value, "Mask Size not recognized."); // Generates compile time error if invalid combination of distance_type and mask_size is used. static_assert(detail::dt_parameters_are_compatible::value, @@ -495,7 +494,7 @@ void distance_transform(SrcView const& src_view, DstView const& dst_view, distan BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); - if(std::is_same::value) + if (std::is_same::value) distance_transform_mask_size_three_impl(src_view, dst_view, dist_from, dist_type); else if (std::is_same::value) From 16d646e251562dc00c8dcc0d00780b0759b7752e Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Wed, 23 Jun 2021 09:03:55 +0530 Subject: [PATCH 15/37] Add some more explanatory and reference comments --- .../image_processing/distance_transform.hpp | 108 +++++++++--------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/include/boost/gil/image_processing/distance_transform.hpp b/include/boost/gil/image_processing/distance_transform.hpp index 1b81641b3a..f39b3ac2cb 100644 --- a/include/boost/gil/image_processing/distance_transform.hpp +++ b/include/boost/gil/image_processing/distance_transform.hpp @@ -91,8 +91,9 @@ struct dt_parameters_are_compatible float constexpr dt_infinite = 1000000000; /// \breif Calculates distance transfrom with mask size three. -/// Optimal local distances a, b for euclidean approximation from, -/// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf +/// Optimal local distances a, b for euclidean approximation from, +/// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf +/// Algorithm from - Principles of Digital Image Processing: Core Algorithms (Section 11.2.2). template void distance_transform_mask_size_three_impl(SrcView const& src_view, DstView const& dst_view, @@ -122,7 +123,7 @@ void distance_transform_mask_size_three_impl(SrcView const& src_view, { for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { - if(dist_from == distance_from::off_pixels) + if (dist_from == distance_from::off_pixels) { if (src_view(x, y)[0] == src_channel_min) intermediate_image_view(x + padding, y + padding)[0] = 0; @@ -139,9 +140,9 @@ void distance_transform_mask_size_three_impl(SrcView const& src_view, } } - float a, b; + float a, b; // a and b hold elements of the distance mask (see reference). - if(std::is_same::value) + if (std::is_same::value) { a = 0.95509f; b = 1.36930f; @@ -161,7 +162,7 @@ void distance_transform_mask_size_three_impl(SrcView const& src_view, { for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { - if(intermediate_image_view(x + padding, y + padding)[0] > 0) + if (intermediate_image_view(x + padding, y + padding)[0] > 0) { float const d1 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; float const d2 = b + intermediate_image_view(x - 1 + padding, y - 1 + padding)[0]; @@ -209,8 +210,9 @@ void distance_transform_mask_size_three_impl(SrcView const& src_view, } /// \breif Calculates distance transfrom with mask size five. -/// Optimal local distances a, b, c for euclidean approximation from, -/// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf +/// Optimal local distances a, b, c for euclidean approximation from, +/// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf +/// Algorithm from - Principles of Digital Image Processing: Core Algorithms (Section 11.2.2). template void distance_transform_mask_size_five_impl(SrcView const& src_view, DstView const& dst_view, @@ -244,7 +246,7 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, { for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { - if(dist_from == distance_from::off_pixels) + if (dist_from == distance_from::off_pixels) { if (src_view(x, y)[0] == src_channel_min) intermediate_image_view(x + padding, y + padding)[0] = 0; @@ -261,15 +263,15 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, } } - float a, b, c; + float a, b, c; // a, b and c hold elements of the distance mask (see reference). - if(std::is_same::value) + if (std::is_same::value) { a = 1; b = 1.4f; c = 2.19691f; } - else if(std::is_same::value) + else if (std::is_same::value) { a = 1; b = 2; @@ -286,7 +288,7 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, { for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { - if(intermediate_image_view(x + padding, y + padding)[0] > 0) + if (intermediate_image_view(x + padding, y + padding)[0] > 0) { float const d1 = c + intermediate_image_view(x - 1 + padding, y - 2 + padding)[0]; float const d2 = c + intermediate_image_view(x + 1 + padding, y - 2 + padding)[0]; @@ -344,47 +346,47 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, } /// \breif Calculates one-dimensional squared euclidean distance. -/// Reference - http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf -template -std::vector calculate_squared_euclidean_distance(ImageIterator f, std::ptrdiff_t n) +/// Reference - http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf (Section 2.1) +template +std::vector compute_1d_squared_euclidean_distance_transform + (ImageViewIterator f, std::ptrdiff_t n) { std::ptrdiff_t k = 0; - std::vector d(n); - std::vector v(n); - std::vector z(n + 1); + std::vector v(n); + std::vector z(n + 1); v[0] = 0; - z[0] = static_cast(-dt_infinite); - z[1] = static_cast(dt_infinite); - for(int q = 1; q < n ; ++q) + z[0] = -dt_infinite; + z[1] = dt_infinite; + for (std::ptrdiff_t q = 1; q < n ; ++q) { float s = static_cast(((f[q][0] + std::pow(q, 2)) - (f[v[k]][0] + std::pow(v[k], 2))) / (2 * q - 2 * v[k])); while (s <= z[k]) { - --k; + k = k - 1; s = static_cast(((f[q][0] + std::pow(q, 2)) - (f[v[k]][0] + std::pow(v[k], 2))) / (2 * q - 2 * v[k])); } - { - k = k + 1; - v[k] = static_cast(q); - z[k] = static_cast(s); - z[k + 1] = static_cast(dt_infinite); - } + k = k + 1; + v[k] = q; + z[k] = s; + z[k + 1] = dt_infinite; } k = 0; - for (int q = 0; q < n; ++q) + std::vector d_f(n); + for (std::ptrdiff_t q = 0; q < n; ++q) { - while(z[k+1] < q) + while (z[k+1] < q) { ++k; } - d[q] = static_cast(std::pow((q - v[k]), 2) + f[v[k]][0]); + d_f[q] = static_cast(std::pow((q - v[k]), 2) + f[v[k]][0]); } - return d; + return d_f; } -/// \breif Calculates precise euclidean distance transform. +/// \breif Calculates two dimensionsal (precise) euclidean distance transform. +/// Reference - http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf (Section 2.2) template void distance_transorm_precise_impl(SrcView const& src_view, DstView const& dst_view, @@ -411,34 +413,38 @@ void distance_transorm_precise_impl(SrcView const& src_view, } } } - - for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + + // Computes one-dimensional distance transforms along each column of the grid (image). + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) { - typename gray32f_view_t::x_iterator intermediate_row_itr = - intermediate_image_view.row_begin(y); + typename gray32f_view_t::y_iterator intermediate_col_itr = + intermediate_image_view.col_begin(x); - std::vector one_dimensional_squared_distance = calculate_squared_euclidean_distance - (intermediate_row_itr, intermediate_image_view.width()); + std::vector one_dimensional_sq_euclidean_dt_along_column = + compute_1d_squared_euclidean_distance_transform + (intermediate_col_itr, intermediate_image_view.height()); - for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) { - intermediate_row_itr[x][0] = one_dimensional_squared_distance[x]; + intermediate_col_itr[y][0] = one_dimensional_sq_euclidean_dt_along_column[y]; } } - for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) + // Computes one-dimensional distance transforms along each row of the grid (image). + for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) { - typename gray32f_view_t::y_iterator intermediate_col_itr = - intermediate_image_view.col_begin(x); + typename gray32f_view_t::x_iterator intermediate_row_itr = + intermediate_image_view.row_begin(y); - std::vector one_dimensional_squared_distance = calculate_squared_euclidean_distance - (intermediate_col_itr, intermediate_image_view.height()); + std::vector one_dimensional_sq_euclidean_dt_along_row = + compute_1d_squared_euclidean_distance_transform + (intermediate_row_itr, intermediate_image_view.width()); - for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) + for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) { - intermediate_col_itr[y][0] = one_dimensional_squared_distance[y]; + intermediate_row_itr[x][0] = one_dimensional_sq_euclidean_dt_along_row[x]; - float const distance_transform = std::sqrt(intermediate_col_itr[y][0]); + float const distance_transform = std::sqrt(intermediate_row_itr[x][0]); float constexpr dst_channel_max = (is_same::value) ? dt_infinite : static_cast( @@ -466,7 +472,7 @@ void distance_transorm_precise_impl(SrcView const& src_view, /// /// \param src_view - Source image view. /// \param dst_view - Destination image view. -/// \param dist_from - Specifies from where to find distance from, neareast, 'on' OR 'off' pixels. +/// \param dist_from - Specifies where to find distance from, neareast, 'on' OR 'off' pixels. /// \param dist_type - Specifies formula/method for distance calculation. /// \tparam SrcView - Source image view type. /// \tparam DstView - Destination image view type. From 2a26f540af1329940d122fd82dd3dec6ef789853 Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Wed, 23 Jun 2021 14:02:44 +0530 Subject: [PATCH 16/37] improve test file formatting --- example/euclidean_distance_transform.cpp | 6 +- .../image_processing/distance_transform.cpp | 166 +++++++++--------- 2 files changed, 87 insertions(+), 85 deletions(-) diff --git a/example/euclidean_distance_transform.cpp b/example/euclidean_distance_transform.cpp index 2869ccbe9e..6826b77866 100644 --- a/example/euclidean_distance_transform.cpp +++ b/example/euclidean_distance_transform.cpp @@ -1,5 +1,5 @@ // -// Copyright 2021 Harsit Pant +// Copyright 2021 Harshit Pant // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -76,6 +76,8 @@ int main(int argc, char* argv[]) } else { - std::cerr << "Incorrect options used."; + std::cerr << "This program fails to work with the set of arguments used. Please try again."; } + + return 0; } diff --git a/test/core/image_processing/distance_transform.cpp b/test/core/image_processing/distance_transform.cpp index ebafdf35c8..2c50d1d61f 100644 --- a/test/core/image_processing/distance_transform.cpp +++ b/test/core/image_processing/distance_transform.cpp @@ -1,5 +1,5 @@ // -// Copyright 2021 Harsit Pant +// Copyright 2021 Harshit Pant // // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -32,7 +32,7 @@ void check_for_negligible_varition(ImgView1 const& image_view1, ImgView2 const& for (std::ptrdiff_t x = 0; x < image_view1.width(); ++x) { float variation = std::abs(image_view1(x, y)[0] - image_view2(x, y)[0]); - BOOST_TEST_LT(variation, 0.1); + BOOST_TEST_LT(variation, 0.001); } } } @@ -44,27 +44,27 @@ void test_manhattan_uint8_t_input_uint8_t_output_distance_from_off_pixels() gil::gray8_image_t img_expected(7, 7); gil::gray8_image_t img_out(7, 7); - //test values from wikipedia, in 8 bit binary form + // test values from wikipedia, in 8 bit binary form std::vector> val_in { - {0, 0, 0, 0, 0, 0,0}, - {0,255,255,255,255,255,0}, - {0,255,255,255,255,255,0}, - {0,255,255,255,255,255,0}, - {0,255,255,255,255,255,0}, - {0,255,255,255,255,255,0}, - {0, 0, 0, 0, 0, 0,0} + {0, 0, 0, 0, 0, 0, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 255, 255, 255, 255, 255, 0}, + {0, 0, 0, 0, 0, 0, 0} }; std::vector> val_exp { - {0,0,0,0,0,0,0}, - {0,1,1,1,1,1,0}, - {0,1,2,2,2,1,0}, - {0,1,2,3,2,1,0}, - {0,1,2,2,2,1,0}, - {0,1,1,1,1,1,0}, - {0,0,0,0,0,0,0} + {0, 0, 0, 0, 0, 0, 0}, + {0, 1, 1, 1, 1, 1, 0}, + {0, 1, 2, 2, 2, 1, 0}, + {0, 1, 2, 3, 2, 1, 0}, + {0, 1, 2, 2, 2, 1, 0}, + {0, 1, 1, 1, 1, 1, 0}, + {0, 0, 0, 0, 0, 0, 0} }; fill_image_view(val_in, view(img_in)); @@ -129,7 +129,7 @@ void test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels() fill_image_view(val_exp_manhattan_five, view(img_expected)); gil::distance_transform(view(img_in), view(img_out), gil::distance_from::on_pixels, - gil::distance_type::manhattan, gil::mask_size::five); + gil::distance_type::manhattan, gil::mask_size::five); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); @@ -147,7 +147,7 @@ void test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels() fill_image_view(val_exp_euclidean_precise, view(img_expected_float32_t)); gil::distance_transform(view(img_in), view(img_out_float32_t), gil::distance_from::on_pixels, - gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); BOOST_TEST(equal_pixels(const_view(img_out_float32_t), const_view(img_expected_float32_t))); } @@ -179,7 +179,7 @@ void test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance std::vector> val_exp1 { - {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f}, + {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f}, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, { 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, { 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, @@ -236,7 +236,7 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() std::vector> val_in { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} , + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255}, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, { 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, @@ -255,36 +255,36 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() std::vector> val_exp_precise { - {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 4.1231055f, 3.1622777f, + {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 4.1231055f, 3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, - { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4, - 3, 2, 1, 0, 1, 2, 3, 4}, - { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 3.1622777f, + { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4, + 3, 2, 1, 0, 1, 2, 3, 4}, + { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, - { 2, 1, 0, 1, 2, 3, 4, 3.6055512f, + { 2, 1, 0, 1, 2, 3, 4, 3.6055512f, 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f}, - { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 4.2426405f, + { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 4.2426405f, 3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5}, - { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f, 5, + { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f, 5, 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f}, - {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5, 5.8309517f, + {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5, 5.8309517f, 5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f}, - { 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f, 6.4031243f, + { 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f, 6.4031243f, 6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.0000005f}, - {5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f, 7.0710683f, + {5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f, 7.0710683f, 7.2801094f, 7.0710683f, 7.0000005f, 6.7082043f, 6.3245554f, 6.0827622f, 6}, - {6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.2111025f, 7.8102503f, + {6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.2111025f, 7.8102503f, 7.8102503f, 7.0710683f, 6.4031243f, 5.8309517f, 5.3851647f, 5.0990195f, 5}, - {7.2801094f, 7.0710683f, 7.0000005f, 7.0710683f, 7.2801094f, 7.6157737f, 8.062258f, 8.062258f, + {7.2801094f, 7.0710683f, 7.0000005f, 7.0710683f, 7.2801094f, 7.6157737f, 8.062258f, 8.062258f, 7.2111025f, 6.4031243f, 5.656854f, 5, 4.472136f, 4.1231055f, 4}, - { 8.246211f, 8.062258f, 8, 8.062258f, 8.246211f, 8.5440035f, 8.5440035f, 7.6157737f, + { 8.246211f, 8.062258f, 8, 8.062258f, 8.246211f, 8.5440035f, 8.5440035f, 7.6157737f, 6.7082043f, 5.8309517f, 5, 4.2426405f, 3.6055512f, 3.1622777f, 3}, - { 9.219544f, 9.055386f, 9, 9.055386f, 9.219544f, 9.219544f, 8.246211f, 7.2801094f, + { 9.219544f, 9.055386f, 9, 9.055386f, 9.219544f, 9.219544f, 8.246211f, 7.2801094f, 6.3245554f, 5.3851647f, 4.472136f, 3.6055512f, 2.828427f, 2.236068f, 2}, - {10.198039f, 10.049875f, 10, 10.049875f, 10.049875f, 9.055386f, 8.062258f, 7.0710683f, + {10.198039f, 10.049875f, 10, 10.049875f, 10.049875f, 9.055386f, 8.062258f, 7.0710683f, 6.0827622f, 5.0990195f, 4.1231055f, 3.1622777f, 2.236068f, 1.4142135f, 1}, - {11.180341f, 11.045361f, 11, 11, 10, 9, 8, 7, - 6, 5, 4, 3, 2, 1, 0} + {11.180341f, 11.045361f, 11, 11, 10, 9, 8, 7, + 6, 5, 4, 3, 2, 1, 0} }; fill_image_view(val_in, view(img_in)); @@ -352,51 +352,51 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() std::vector> val_exp_euclidean_five { - {3.5969f, 3.1969f, 3, 3.1969f, 3.5969f, 4.2f, - 4.1969f, 3.1969f, 2.1969f, 1.4f, 1, 1.4f, - 2.1969f, 3.1969f, 4.1969f}, - { 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, - 4, 3, 2, 1, 0, 1, - 2, 3, 4}, - {2.1969f, 1.4f, 1, 1.4f, 2.1969f, 3.1969f, - 4.1969f, 3.1969f, 2.1969f, 1.4f, 1, 1.4f, - 2.1969f, 3.1969f, 4.1969f}, - { 2, 1, 0, 1, 2, 3, - 4, 3.5969f, 2.8f, 2.1969f, 2, 2.1969f, - 2.8f, 3.5969f, 4.3938f}, - {2.1969f, 1.4f, 1, 1.4f, 2.1969f, 3.1969f, - 4.1969f, 4.2f, 3.5969f, 3.1969f, 3, 3.1969f, - 3.5969f, 4.2f, 4.9968996f}, - { 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, - 4.3938f, 4.9968996f, 4.3938f, 4.1969f, 4, 4.1969f, - 4.3938f, 4.9968996f, 5.6f}, - {3.5969f, 3.1969f, 3, 3.1969f, 3.5969f, 4.2f, - 4.9968996f, 5.7938f, 5.3938f, 5.1969f, 5, 5.1969f, - 5.3938f, 5.7938f, 6.3968997f}, - {4.3938f, 4.1969f, 4, 4.1969f, 4.3938f, 4.9968996f, - 5.6f, 6.3968997f, 6.3938f, 6.1969f, 6, 6.1969f, - 6.3938f, 6.5906997f, 7}, - {5.3938f, 5.1969f, 5, 5.1969f, 5.3938f, 5.7938f, - 6.3968997f, 7, 7.3938f, 7.1969f, 7, 6.5906997f, - 6.3938f, 6.1969f, 6}, - {6.3938f, 6.1969f, 6, 6.1969f, 6.3938f, 6.5906997f, - 7.1937995f, 7.7969f, 7.7969f, 7, 6.3968997f, 5.7938f, - 5.3938f, 5.1969f, 5}, - {7.3938f, 7.1969f, 7, 7.1969f, 7.3938f, 7.5906997f, - 7.9907f, 7.9907f, 7.1937995f, 6.3968997f, 5.6f, 4.9968996f, - 4.3938f, 4.1969f, 4}, - {8.3938f, 8.196899f, 8, 8.196899f, 8.3938f, 8.5907f, - 8.5907f, 7.5906997f, 6.5906997f, 5.7938f, 4.9968996f, 4.2f, - 3.5969f, 3.1969f, 3}, - {9.3938f, 9.196899f, 9, 9.196899f, 9.3938f, 9.3938f, - 8.3938f, 7.3938f, 6.3938f, 5.3938f, 4.3938f, 3.5969f, - 2.8f, 2.1969f, 2}, - {10.393799f, 10.196899f, 10, 10.196899f, 10.196899f, 9.196899f, - 8.196899f, 7.1969f, 6.1969f, 5.1969f, 4.1969f, 3.1969f, - 2.1969f, 1.4f, 1}, - {11.393799f, 11.196899f, 11, 11, 10, 9, - 8, 7, 6, 5, 4, 3, - 2, 1, 0} + { 3.5969f, 3.1969f, 3, 3.1969f, 3.5969f, 4.2f, + 4.1969f, 3.1969f, 2.1969f, 1.4f, 1, 1.4f, + 2.1969f, 3.1969f, 4.1969f}, + { 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, + 4, 3, 2, 1, 0, 1, + 2, 3, 4}, + { 2.1969f, 1.4f, 1, 1.4f, 2.1969f, 3.1969f, + 4.1969f, 3.1969f, 2.1969f, 1.4f, 1, 1.4f, + 2.1969f, 3.1969f, 4.1969f}, + { 2, 1, 0, 1, 2, 3, + 4, 3.5969f, 2.8f, 2.1969f, 2, 2.1969f, + 2.8f, 3.5969f, 4.3938f}, + { 2.1969f, 1.4f, 1, 1.4f, 2.1969f, 3.1969f, + 4.1969f, 4.2f, 3.5969f, 3.1969f, 3, 3.1969f, + 3.5969f, 4.2f, 4.9968996f}, + { 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, + 4.3938f, 4.9968996f, 4.3938f, 4.1969f, 4, 4.1969f, + 4.3938f, 4.9968996f, 5.6f}, + { 3.5969f, 3.1969f, 3, 3.1969f, 3.5969f, 4.2f, + 4.9968996f, 5.7938f, 5.3938f, 5.1969f, 5, 5.1969f, + 5.3938f, 5.7938f, 6.3968997f}, + { 4.3938f, 4.1969f, 4, 4.1969f, 4.3938f, 4.9968996f, + 5.6f, 6.3968997f, 6.3938f, 6.1969f, 6, 6.1969f, + 6.3938f, 6.5906997f, 7}, + { 5.3938f, 5.1969f, 5, 5.1969f, 5.3938f, 5.7938f, + 6.3968997f, 7, 7.3938f, 7.1969f, 7, 6.5906997f, + 6.3938f, 6.1969f, 6}, + { 6.3938f, 6.1969f, 6, 6.1969f, 6.3938f, 6.5906997f, + 7.1937995f, 7.7969f, 7.7969f, 7, 6.3968997f, 5.7938f, + 5.3938f, 5.1969f, 5}, + { 7.3938f, 7.1969f, 7, 7.1969f, 7.3938f, 7.5906997f, + 7.9907f, 7.9907f, 7.1937995f, 6.3968997f, 5.6f, 4.9968996f, + 4.3938f, 4.1969f, 4}, + { 8.3938f, 8.196899f, 8, 8.196899f, 8.3938f, 8.5907f, + 8.5907f, 7.5906997f, 6.5906997f, 5.7938f, 4.9968996f, 4.2f, + 3.5969f, 3.1969f, 3}, + { 9.3938f, 9.196899f, 9, 9.196899f, 9.3938f, 9.3938f, + 8.3938f, 7.3938f, 6.3938f, 5.3938f, 4.3938f, 3.5969f, + 2.8f, 2.1969f, 2}, + {10.393799f, 10.196899f, 10, 10.196899f, 10.196899f, 9.196899f, + 8.196899f, 7.1969f, 6.1969f, 5.1969f, 4.1969f, 3.1969f, + 2.1969f, 1.4f, 1}, + {11.393799f, 11.196899f, 11, 11, 10, 9, + 8, 7, 6, 5, 4, 3, + 2, 1, 0} }; fill_image_view(val_in, view(img_in)); From ef173b11ef23c1059c79b81e6511405e8d7de83c Mon Sep 17 00:00:00 2001 From: harsh-4 Date: Wed, 23 Jun 2021 19:33:14 +0530 Subject: [PATCH 17/37] Use clang-format for formatting Uses the clang-format configuration provided in the latest commit of PR #596. Formatting is applied to test file and example file as well. I have made no changes whatsoever after using the clang-format. --- example/euclidean_distance_transform.cpp | 74 +- .../image_processing/distance_transform.hpp | 414 +++--- .../image_processing/distance_transform.cpp | 1173 ++++++++++++----- 3 files changed, 1126 insertions(+), 535 deletions(-) diff --git a/example/euclidean_distance_transform.cpp b/example/euclidean_distance_transform.cpp index 6826b77866..50cac6aab0 100644 --- a/example/euclidean_distance_transform.cpp +++ b/example/euclidean_distance_transform.cpp @@ -5,10 +5,10 @@ // 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 @@ -20,8 +20,9 @@ int main(int argc, char* argv[]) { if (argc != 6) { - std::cerr << "usage: " << argv[0] << " " - " "; + std::cerr << "usage: " << argv[0] + << " " + " "; return -1; } @@ -35,43 +36,74 @@ int main(int argc, char* argv[]) if (dist_from == "on_pixels" && dist_type == "euclidean_approximation" && mask_size == "three") { - distance_transform(view(input), view(output), gil::distance_from::on_pixels, - gil::distance_type::euclidean_approximation, gil::mask_size::three); + distance_transform( + view(input), + view(output), + gil::distance_from::on_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::three); gil::write_view(argv[2], view(output), gil::png_tag{}); } - else if (dist_from == "off_pixels" && dist_type == "euclidean_approximation" && mask_size == "three") + else if ( + dist_from == "off_pixels" && dist_type == "euclidean_approximation" && mask_size == "three") { - distance_transform(view(input), view(output), gil::distance_from::off_pixels, - gil::distance_type::euclidean_approximation, gil::mask_size::three); + distance_transform( + view(input), + view(output), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::three); gil::write_view(argv[2], view(output), gil::png_tag{}); } - else if (dist_from == "on_pixels" && dist_type == "euclidean_approximation" && mask_size == "five") + else if ( + dist_from == "on_pixels" && dist_type == "euclidean_approximation" && mask_size == "five") { - distance_transform(view(input), view(output), gil::distance_from::on_pixels, - gil::distance_type::euclidean_approximation, gil::mask_size::five); + distance_transform( + view(input), + view(output), + gil::distance_from::on_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::five); gil::write_view(argv[2], view(output), gil::png_tag{}); } - else if (dist_from == "off_pixels" && dist_type == "euclidean_approximation" && mask_size == "five") + else if ( + dist_from == "off_pixels" && dist_type == "euclidean_approximation" && mask_size == "five") { - distance_transform(view(input), view(output), gil::distance_from::off_pixels, - gil::distance_type::euclidean_approximation, gil::mask_size::five); + distance_transform( + view(input), + view(output), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::five); gil::write_view(argv[2], view(output), gil::png_tag{}); } - else if (dist_from == "on_pixels" && dist_type == "precise_euclidean" && mask_size == "not_applicable") + else if ( + dist_from == "on_pixels" && dist_type == "precise_euclidean" + && mask_size == "not_applicable") { - distance_transform(view(input), view(output), gil::distance_from::on_pixels, - gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + distance_transform( + view(input), + view(output), + gil::distance_from::on_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); gil::write_view(argv[2], view(output), gil::png_tag{}); } - else if (dist_from == "off_pixels" && dist_type == "precise_euclidean" && mask_size == "not_applicable") + else if ( + dist_from == "off_pixels" && dist_type == "precise_euclidean" + && mask_size == "not_applicable") { - distance_transform(view(input), view(output), gil::distance_from::off_pixels, - gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + distance_transform( + view(input), + view(output), + gil::distance_from::off_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); gil::write_view(argv[2], view(output), gil::png_tag{}); } else diff --git a/include/boost/gil/image_processing/distance_transform.hpp b/include/boost/gil/image_processing/distance_transform.hpp index f39b3ac2cb..96d22e3929 100644 --- a/include/boost/gil/image_processing/distance_transform.hpp +++ b/include/boost/gil/image_processing/distance_transform.hpp @@ -22,26 +22,40 @@ namespace boost { namespace gil { namespace distance_type { -struct euclidean_approximation_t {}; -struct manhattan_t {}; -struct chessboard_t {}; -struct precise_euclidean_t {}; +struct euclidean_approximation_t +{ +}; +struct manhattan_t +{ +}; +struct chessboard_t +{ +}; +struct precise_euclidean_t +{ +}; static euclidean_approximation_t euclidean_approximation; static manhattan_t manhattan; static chessboard_t chessboard; static precise_euclidean_t precise_euclidean; -} // namespace distance_type +} // namespace distance_type namespace mask_size { -struct three_t {}; -struct five_t {}; -struct not_applicable_t {}; +struct three_t +{ +}; +struct five_t +{ +}; +struct not_applicable_t +{ +}; static three_t three; static five_t five; static not_applicable_t not_applicable; -} // namespace mask_size +} // namespace mask_size enum class distance_from { @@ -55,37 +69,43 @@ namespace detail { template struct dt_check_mask_size { - static constexpr bool value = std::is_same::value || - std::is_same::value || - std::is_same::value; + static constexpr bool value = std::is_same::value + || std::is_same::value + || std::is_same::value; }; /// \breif Checks if distance_type used exists. template struct dt_check_distance_type { - static constexpr bool value = - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value; + static constexpr bool value + = std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value; }; /// \breif Checks compatiblity of distance_type and mask_size used together. template -struct dt_parameters_are_compatible : std::true_type {}; +struct dt_parameters_are_compatible : std::true_type +{ +}; template -struct dt_parameters_are_compatible - : std::false_type {}; +struct dt_parameters_are_compatible : std::false_type +{ +}; template -struct dt_parameters_are_compatible - : std::false_type {}; +struct dt_parameters_are_compatible : std::false_type +{ +}; template <> -struct dt_parameters_are_compatible - : std::true_type {}; +struct dt_parameters_are_compatible + : std::true_type +{ +}; /// \breif Value used as infinite distance for distance_transform. float constexpr dt_infinite = 1000000000; @@ -94,30 +114,31 @@ float constexpr dt_infinite = 1000000000; /// Optimal local distances a, b for euclidean approximation from, /// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf /// Algorithm from - Principles of Digital Image Processing: Core Algorithms (Section 11.2.2). -template -void distance_transform_mask_size_three_impl(SrcView const& src_view, - DstView const& dst_view, - distance_from dist_from, - DistanceType /*Passes type*/) +template +void distance_transform_mask_size_three_impl( + SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from, + DistanceType /*Passes type*/) { int const padding = 1; - gray32f_image_t intermediate_image(src_view.width() + 2 * padding, - src_view.height() + 2 * padding); - gray32f_view_t intermediate_image_view = view(intermediate_image); + gray32f_image_t intermediate_image( + src_view.width() + 2 * padding, src_view.height() + 2 * padding); + gray32f_view_t intermediate_image_view = view(intermediate_image); for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) { - intermediate_image_view(x, 0)[0] = dt_infinite ; + intermediate_image_view(x, 0)[0] = dt_infinite; intermediate_image_view(x, intermediate_image_view.height() - 1)[0] = dt_infinite; } for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) { - intermediate_image_view(0, y)[0] = dt_infinite ; - intermediate_image_view(intermediate_image_view.width() - 1, y)[0] = dt_infinite ; + intermediate_image_view(0, y)[0] = dt_infinite; + intermediate_image_view(intermediate_image_view.width() - 1, y)[0] = dt_infinite; } - float constexpr src_channel_min = - (std::numeric_limits ::type>::min)(); + float constexpr src_channel_min + = (std::numeric_limits::type>::min)(); for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { @@ -127,33 +148,33 @@ void distance_transform_mask_size_three_impl(SrcView const& src_view, { if (src_view(x, y)[0] == src_channel_min) intermediate_image_view(x + padding, y + padding)[0] = 0; - else + else intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; } else { - if (src_view(x, y)[0] != src_channel_min) + if (src_view(x, y)[0] != src_channel_min) intermediate_image_view(x + padding, y + padding)[0] = 0; - else + else intermediate_image_view(x + padding, y + padding)[0] = dt_infinite; } } } - float a, b; // a and b hold elements of the distance mask (see reference). + float a, b; // a and b hold elements of the distance mask (see reference). if (std::is_same::value) { a = 0.95509f; b = 1.36930f; } - else if(std::is_same::value) - { + else if (std::is_same::value) + { a = 1; b = 2; } else - { + { a = 1; b = 1; } @@ -163,10 +184,10 @@ void distance_transform_mask_size_three_impl(SrcView const& src_view, for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { if (intermediate_image_view(x + padding, y + padding)[0] > 0) - { - float const d1 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; + { + float const d1 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; float const d2 = b + intermediate_image_view(x - 1 + padding, y - 1 + padding)[0]; - float const d3 = a + intermediate_image_view(x + padding, y - 1 + padding)[0]; + float const d3 = a + intermediate_image_view(x + padding, y - 1 + padding)[0]; float const d4 = b + intermediate_image_view(x + 1 + padding, y - 1 + padding)[0]; intermediate_image_view(x + padding, y + padding)[0] = std::min({d1, d2, d3, d4}); @@ -180,48 +201,57 @@ void distance_transform_mask_size_three_impl(SrcView const& src_view, { if (intermediate_image_view(x + padding, y + padding)[0] > 0) { - float const d1 = a + intermediate_image_view(x + 1 + padding, y + padding)[0]; + float const d1 = a + intermediate_image_view(x + 1 + padding, y + padding)[0]; float const d2 = b + intermediate_image_view(x + 1 + padding, y + 1 + padding)[0]; - float const d3 = a + intermediate_image_view(x + padding, y + 1 + padding)[0]; + float const d3 = a + intermediate_image_view(x + padding, y + 1 + padding)[0]; float const d4 = b + intermediate_image_view(x - 1 + padding, y + 1 + padding)[0]; - intermediate_image_view(x + padding, y + padding)[0] = std::min({ d1, d2, d3, d4, - static_cast(intermediate_image_view(x + padding, y + padding)[0]) }); - - float const distance_transform = intermediate_image_view - (x + padding, y + padding)[0]; - - float constexpr dst_channel_max = - (is_same::value) ? dt_infinite : static_cast( - (std::numeric_limits::type>::max)()); - - float constexpr dst_channel_min = - (is_same::value) ? 0.f : static_cast( - (std::numeric_limits::type>::min)()); - - dst_view(x, y)[0] = - static_cast::type>( - (distance_transform > dst_channel_max) ? dst_channel_max : - (distance_transform < dst_channel_min) ? dst_channel_min : - distance_transform); + intermediate_image_view(x + padding, y + padding)[0] = std::min( + {d1, + d2, + d3, + d4, + static_cast(intermediate_image_view(x + padding, y + padding)[0])}); + + float const distance_transform + = intermediate_image_view(x + padding, y + padding)[0]; + + float constexpr dst_channel_max + = (is_same::value) + ? dt_infinite + : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min + = (is_same::value) + ? 0.f + : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] + = static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max + : (distance_transform < dst_channel_min) ? dst_channel_min + : distance_transform); } } } -} +} /// \breif Calculates distance transfrom with mask size five. /// Optimal local distances a, b, c for euclidean approximation from, /// http://www.cmm.mines-paristech.fr/~marcoteg/cv/publi_pdf/MM_refs/1986_Borgefors_distance.pdf /// Algorithm from - Principles of Digital Image Processing: Core Algorithms (Section 11.2.2). -template -void distance_transform_mask_size_five_impl(SrcView const& src_view, - DstView const& dst_view, - distance_from dist_from, - DistanceType /*Passes type*/) +template +void distance_transform_mask_size_five_impl( + SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from, + DistanceType /*Passes type*/) { int const padding = 2; - gray32f_image_t intermediate_image(src_view.width() + 2 * padding, - src_view.height() + 2 * padding); + gray32f_image_t intermediate_image( + src_view.width() + 2 * padding, src_view.height() + 2 * padding); gray32f_view_t intermediate_image_view = view(intermediate_image); for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) @@ -233,14 +263,14 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, } for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) { - intermediate_image_view(0, y)[0] = dt_infinite ; - intermediate_image_view(1, y)[0] = dt_infinite ; + intermediate_image_view(0, y)[0] = dt_infinite; + intermediate_image_view(1, y)[0] = dt_infinite; intermediate_image_view(intermediate_image_view.width() - 1, y)[0] = dt_infinite; intermediate_image_view(intermediate_image_view.width() - 2, y)[0] = dt_infinite; } - float constexpr src_channel_min = - (std::numeric_limits::type>::min)(); + float constexpr src_channel_min + = (std::numeric_limits::type>::min)(); for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { @@ -263,7 +293,7 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, } } - float a, b, c; // a, b and c hold elements of the distance mask (see reference). + float a, b, c; // a, b and c hold elements of the distance mask (see reference). if (std::is_same::value) { @@ -272,13 +302,13 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, c = 2.19691f; } else if (std::is_same::value) - { + { a = 1; b = 2; c = 3; } else - { + { a = 1; b = 1; c = 2; @@ -289,18 +319,18 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { if (intermediate_image_view(x + padding, y + padding)[0] > 0) - { + { float const d1 = c + intermediate_image_view(x - 1 + padding, y - 2 + padding)[0]; float const d2 = c + intermediate_image_view(x + 1 + padding, y - 2 + padding)[0]; float const d3 = c + intermediate_image_view(x - 2 + padding, y - 1 + padding)[0]; float const d4 = b + intermediate_image_view(x - 1 + padding, y - 1 + padding)[0]; - float const d5 = a + intermediate_image_view(x + padding, y - 1 + padding)[0]; + float const d5 = a + intermediate_image_view(x + padding, y - 1 + padding)[0]; float const d6 = b + intermediate_image_view(x + 1 + padding, y - 1 + padding)[0]; float const d7 = c + intermediate_image_view(x + 2 + padding, y - 1 + padding)[0]; - float const d8 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; + float const d8 = a + intermediate_image_view(x - 1 + padding, y + padding)[0]; - intermediate_image_view(x + padding, y + padding)[0] = - std::min({d1, d2, d3, d4, d5, d6, d7, d8}); + intermediate_image_view(x + padding, y + padding)[0] + = std::min({d1, d2, d3, d4, d5, d6, d7, d8}); } } } @@ -315,41 +345,53 @@ void distance_transform_mask_size_five_impl(SrcView const& src_view, float const d2 = c + intermediate_image_view(x - 1 + padding, y + 2 + padding)[0]; float const d3 = c + intermediate_image_view(x + 2 + padding, y + 1 + padding)[0]; float const d4 = b + intermediate_image_view(x + 1 + padding, y + 1 + padding)[0]; - float const d5 = a + intermediate_image_view(x + padding, y + 1 + padding)[0]; + float const d5 = a + intermediate_image_view(x + padding, y + 1 + padding)[0]; float const d6 = b + intermediate_image_view(x - 1 + padding, y + 1 + padding)[0]; float const d7 = c + intermediate_image_view(x - 2 + padding, y + 1 + padding)[0]; - float const d8 = a + intermediate_image_view(x + 1 + padding, y + padding)[0]; - - intermediate_image_view(x + padding, y + padding)[0] = - std::min({ d1, d2, d3, d4, d5, d6, d7, d8, - static_cast(intermediate_image_view(x + padding, y + padding)[0]) }); - - float const distance_transform = - intermediate_image_view (x + padding, y + padding)[0]; - - float constexpr dst_channel_max = - (is_same::value) ? dt_infinite : static_cast( - (std::numeric_limits::type>::max)()); - - float constexpr dst_channel_min = - (is_same::value) ? 0.f : static_cast( - (std::numeric_limits::type>::min)()); - - dst_view(x, y)[0] = - static_cast::type>( - (distance_transform > dst_channel_max) ? dst_channel_max : - (distance_transform < dst_channel_min) ? dst_channel_min : - distance_transform); + float const d8 = a + intermediate_image_view(x + 1 + padding, y + padding)[0]; + + intermediate_image_view(x + padding, y + padding)[0] = std::min( + {d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + static_cast(intermediate_image_view(x + padding, y + padding)[0])}); + + float const distance_transform + = intermediate_image_view(x + padding, y + padding)[0]; + + float constexpr dst_channel_max + = (is_same::value) + ? dt_infinite + : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min + = (is_same::value) + ? 0.f + : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] + = static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max + : (distance_transform < dst_channel_min) ? dst_channel_min + : distance_transform); } } } -} +} /// \breif Calculates one-dimensional squared euclidean distance. /// Reference - http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf (Section 2.1) -template -std::vector compute_1d_squared_euclidean_distance_transform - (ImageViewIterator f, std::ptrdiff_t n) +template +std::vector compute_1d_squared_euclidean_distance_transform( + ImageViewIterator f, + std::ptrdiff_t n) { std::ptrdiff_t k = 0; std::vector v(n); @@ -357,27 +399,28 @@ std::vector compute_1d_squared_euclidean_distance_transform v[0] = 0; z[0] = -dt_infinite; z[1] = dt_infinite; - for (std::ptrdiff_t q = 1; q < n ; ++q) + for (std::ptrdiff_t q = 1; q < n; ++q) { - float s = static_cast(((f[q][0] + std::pow(q, 2)) - - (f[v[k]][0] + std::pow(v[k], 2))) / (2 * q - 2 * v[k])); + float s = static_cast( + ((f[q][0] + std::pow(q, 2)) - (f[v[k]][0] + std::pow(v[k], 2))) / (2 * q - 2 * v[k])); while (s <= z[k]) { k = k - 1; - s = static_cast(((f[q][0] + std::pow(q, 2)) - - (f[v[k]][0] + std::pow(v[k], 2))) / (2 * q - 2 * v[k])); + s = static_cast( + ((f[q][0] + std::pow(q, 2)) - (f[v[k]][0] + std::pow(v[k], 2))) + / (2 * q - 2 * v[k])); } - k = k + 1; - v[k] = q; - z[k] = s; + k = k + 1; + v[k] = q; + z[k] = s; z[k + 1] = dt_infinite; } k = 0; - std::vector d_f(n); + std::vector d_f(n); for (std::ptrdiff_t q = 0; q < n; ++q) { - while (z[k+1] < q) - { + while (z[k + 1] < q) + { ++k; } d_f[q] = static_cast(std::pow((q - v[k]), 2) + f[v[k]][0]); @@ -387,15 +430,16 @@ std::vector compute_1d_squared_euclidean_distance_transform /// \breif Calculates two dimensionsal (precise) euclidean distance transform. /// Reference - http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf (Section 2.2) -template -void distance_transorm_precise_impl(SrcView const& src_view, - DstView const& dst_view, - distance_from dist_from) +template +void distance_transorm_precise_impl( + SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from) { gray32f_image_t intermediate_image(src_view.dimensions()); - gray32f_view_t intermediate_image_view = view(intermediate_image); - float constexpr src_channel_min = - (std::numeric_limits::type>::min)(); + gray32f_view_t intermediate_image_view = view(intermediate_image); + float constexpr src_channel_min + = (std::numeric_limits::type>::min)(); for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { @@ -403,26 +447,30 @@ void distance_transorm_precise_impl(SrcView const& src_view, { if (dist_from == distance_from::off_pixels) { - if (src_view(x, y)[0] == src_channel_min) intermediate_image_view(x, y)[0] = 0; - else intermediate_image_view(x, y)[0] = dt_infinite; + if (src_view(x, y)[0] == src_channel_min) + intermediate_image_view(x, y)[0] = 0; + else + intermediate_image_view(x, y)[0] = dt_infinite; } else { - if (src_view(x, y)[0] != src_channel_min) intermediate_image_view(x, y)[0] = 0; - else intermediate_image_view(x, y)[0] = dt_infinite; + if (src_view(x, y)[0] != src_channel_min) + intermediate_image_view(x, y)[0] = 0; + else + intermediate_image_view(x, y)[0] = dt_infinite; } } } - + // Computes one-dimensional distance transforms along each column of the grid (image). for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) { - typename gray32f_view_t::y_iterator intermediate_col_itr = - intermediate_image_view.col_begin(x); + typename gray32f_view_t::y_iterator intermediate_col_itr + = intermediate_image_view.col_begin(x); - std::vector one_dimensional_sq_euclidean_dt_along_column = - compute_1d_squared_euclidean_distance_transform - (intermediate_col_itr, intermediate_image_view.height()); + std::vector one_dimensional_sq_euclidean_dt_along_column + = compute_1d_squared_euclidean_distance_transform( + intermediate_col_itr, intermediate_image_view.height()); for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) { @@ -433,43 +481,47 @@ void distance_transorm_precise_impl(SrcView const& src_view, // Computes one-dimensional distance transforms along each row of the grid (image). for (std::ptrdiff_t y = 0; y < intermediate_image_view.height(); ++y) { - typename gray32f_view_t::x_iterator intermediate_row_itr = - intermediate_image_view.row_begin(y); + typename gray32f_view_t::x_iterator intermediate_row_itr + = intermediate_image_view.row_begin(y); - std::vector one_dimensional_sq_euclidean_dt_along_row = - compute_1d_squared_euclidean_distance_transform - (intermediate_row_itr, intermediate_image_view.width()); + std::vector one_dimensional_sq_euclidean_dt_along_row + = compute_1d_squared_euclidean_distance_transform( + intermediate_row_itr, intermediate_image_view.width()); for (std::ptrdiff_t x = 0; x < intermediate_image_view.width(); ++x) { intermediate_row_itr[x][0] = one_dimensional_sq_euclidean_dt_along_row[x]; - - float const distance_transform = std::sqrt(intermediate_row_itr[x][0]); - float constexpr dst_channel_max = - (is_same::value) ? dt_infinite : static_cast( - (std::numeric_limits::type>::max)()); - - float constexpr dst_channel_min = - (is_same::value) ? 0.f : static_cast( - (std::numeric_limits::type>::min)()); + float const distance_transform = std::sqrt(intermediate_row_itr[x][0]); - dst_view(x, y)[0] = - static_cast::type>( - (distance_transform > dst_channel_max) ? dst_channel_max : - (distance_transform < dst_channel_min) ? dst_channel_min : - distance_transform); + float constexpr dst_channel_max + = (is_same::value) + ? dt_infinite + : static_cast( + (std::numeric_limits::type>::max)()); + + float constexpr dst_channel_min + = (is_same::value) + ? 0.f + : static_cast( + (std::numeric_limits::type>::min)()); + + dst_view(x, y)[0] + = static_cast::type>( + (distance_transform > dst_channel_max) ? dst_channel_max + : (distance_transform < dst_channel_min) ? dst_channel_min + : distance_transform); } } } -} // namespace detail +} // namespace detail /// \addtogroup ImageProcessing /// @{ -/// \breif Calculates distance to the nearest 'on' OR 'off' pixel (as specified), +/// \breif Calculates distance to the nearest 'on' OR 'off' pixel (as specified), /// for each pixel of a binary image. -/// +/// /// \param src_view - Source image view. /// \param dst_view - Destination image view. /// \param dist_from - Specifies where to find distance from, neareast, 'on' OR 'off' pixels. @@ -478,25 +530,31 @@ void distance_transorm_precise_impl(SrcView const& src_view, /// \tparam DstView - Destination image view type. /// \tparam DistanceType - Distance type choosen. /// \tparam MaskSize - Mask size choosen. -template -void distance_transform(SrcView const& src_view, DstView const& dst_view, distance_from dist_from, - DistanceType dist_type, MaskSize /*Passes type*/) +template +void distance_transform( + SrcView const& src_view, + DstView const& dst_view, + distance_from dist_from, + DistanceType dist_type, + MaskSize /*Passes type*/) { gil_function_requires>(); gil_function_requires>(); - static_assert(std::is_same::type>::value && - std::is_same::type>::value, - "Source and destination image views must use gray color space."); + static_assert( + std::is_same::type>::value + && std::is_same::type>::value, + "Source and destination image views must use gray color space."); - static_assert(detail::dt_check_distance_type::value, - "Distance type not recognized."); + static_assert( + detail::dt_check_distance_type::value, "Distance type not recognized."); static_assert(detail::dt_check_mask_size::value, "Mask Size not recognized."); // Generates compile time error if invalid combination of distance_type and mask_size is used. - static_assert(detail::dt_parameters_are_compatible::value, - "distance_transform is incompatible with combination of " - "distance_type and mask_size used."); + static_assert( + detail::dt_parameters_are_compatible::value, + "distance_transform is incompatible with combination of " + "distance_type and mask_size used."); BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions()); @@ -510,5 +568,5 @@ void distance_transform(SrcView const& src_view, DstView const& dst_view, distan distance_transorm_precise_impl(src_view, dst_view, dist_from); } /// @} -}} // namespace boost::gil -#endif // BOOST_GIL_IMAGE_PROCESSING_DISTANCE_TRANSFORM_HPP +}} // namespace boost::gil +#endif // BOOST_GIL_IMAGE_PROCESSING_DISTANCE_TRANSFORM_HPP diff --git a/test/core/image_processing/distance_transform.cpp b/test/core/image_processing/distance_transform.cpp index 2c50d1d61f..effe58fe1d 100644 --- a/test/core/image_processing/distance_transform.cpp +++ b/test/core/image_processing/distance_transform.cpp @@ -5,15 +5,17 @@ // 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 fill_image_view(std::vector >& values, ImgView image_view) +template +void fill_image_view(std::vector>& values, ImgView image_view) { for (std::ptrdiff_t y = 0; y < image_view.height(); ++y) { @@ -24,8 +26,8 @@ void fill_image_view(std::vector >& values, ImgView image_view) } } -template -void check_for_negligible_varition(ImgView1 const& image_view1, ImgView2 const& image_view2) +template +void check_for_negligible_varition(ImgView1 const& image_view1, ImgView2 const& image_view2) { for (std::ptrdiff_t y = 0; y < image_view1.height(); ++y) { @@ -39,39 +41,38 @@ void check_for_negligible_varition(ImgView1 const& image_view1, ImgView2 const& void test_manhattan_uint8_t_input_uint8_t_output_distance_from_off_pixels() { - gil::gray8_image_t img_in(7, 7); gil::gray8_image_t img_expected(7, 7); gil::gray8_image_t img_out(7, 7); // test values from wikipedia, in 8 bit binary form - std::vector> val_in - { - {0, 0, 0, 0, 0, 0, 0}, + std::vector> val_in{ + {0, 0, 0, 0, 0, 0, 0}, {0, 255, 255, 255, 255, 255, 0}, {0, 255, 255, 255, 255, 255, 0}, {0, 255, 255, 255, 255, 255, 0}, {0, 255, 255, 255, 255, 255, 0}, {0, 255, 255, 255, 255, 255, 0}, - {0, 0, 0, 0, 0, 0, 0} - }; + {0, 0, 0, 0, 0, 0, 0}}; - std::vector> val_exp - { + std::vector> val_exp{ {0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 0}, {0, 1, 2, 2, 2, 1, 0}, {0, 1, 2, 3, 2, 1, 0}, {0, 1, 2, 2, 2, 1, 0}, {0, 1, 1, 1, 1, 1, 0}, - {0, 0, 0, 0, 0, 0, 0} - }; + {0, 0, 0, 0, 0, 0, 0}}; fill_image_view(val_in, view(img_in)); fill_image_view(val_exp, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::manhattan, gil::mask_size::three); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::manhattan, + gil::mask_size::three); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); } @@ -85,69 +86,73 @@ void test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels() gil::gray32f_image_t img_expected_float32_t(7, 7); gil::gray32f_image_t img_out_float32_t(7, 7); - std::vector> val_in - { - {0, 65535, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 65535, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} - }; - - std::vector> val_exp_chessboard_three - { + std::vector> val_in{ + {0, 65535, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 65535, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0}}; + + std::vector> val_exp_chessboard_three{ {1, 0, 1, 2, 3, 4, 4}, {1, 1, 1, 2, 3, 3, 3}, {2, 2, 2, 2, 2, 2, 3}, {3, 2, 1, 1, 1, 2, 3}, {3, 2, 1, 0, 1, 2, 3}, {3, 2, 1, 1, 1, 2, 3}, - {3, 2, 2, 2, 2, 2, 3} - }; + {3, 2, 2, 2, 2, 2, 3}}; fill_image_view(val_in, view(img_in)); fill_image_view(val_exp_chessboard_three, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::on_pixels, - gil::distance_type::chessboard, gil::mask_size::three); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::on_pixels, + gil::distance_type::chessboard, + gil::mask_size::three); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); - std::vector> val_exp_manhattan_five - { + std::vector> val_exp_manhattan_five{ {1, 0, 1, 2, 3, 4, 5}, {2, 1, 2, 3, 4, 5, 6}, {3, 2, 3, 2, 3, 4, 5}, {4, 3, 2, 1, 2, 3, 4}, {3, 2, 1, 0, 1, 2, 3}, {4, 3, 2, 1, 2, 3, 4}, - {5, 4, 3, 2, 3, 4, 5} - }; + {5, 4, 3, 2, 3, 4, 5}}; fill_image_view(val_exp_manhattan_five, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::on_pixels, - gil::distance_type::manhattan, gil::mask_size::five); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::on_pixels, + gil::distance_type::manhattan, + gil::mask_size::five); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); - std::vector> val_exp_euclidean_precise - { - { 1, 0, 1, 2, 3, 4, 5}, - {1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 3.6055512f, 4.2426405f}, - { 2.236068f, 2, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f}, - {3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f}, - { 3, 2, 1, 0, 1, 2, 3}, - {3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f}, - {3.6055512f, 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f} - }; + std::vector> val_exp_euclidean_precise{ + {1, 0, 1, 2, 3, 4, 5}, + {1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 3.6055512f, 4.2426405f}, + {2.236068f, 2, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f}, + {3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f}, + {3, 2, 1, 0, 1, 2, 3}, + {3.1622777f, 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f}, + {3.6055512f, 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f}}; fill_image_view(val_exp_euclidean_precise, view(img_expected_float32_t)); - gil::distance_transform(view(img_in), view(img_out_float32_t), gil::distance_from::on_pixels, - gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + gil::distance_transform( + view(img_in), + view(img_out_float32_t), + gil::distance_from::on_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); BOOST_TEST(equal_pixels(const_view(img_out_float32_t), const_view(img_expected_float32_t))); } @@ -158,46 +163,41 @@ void test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance gil::gray32f_image_t img_expected(15, 15); gil::gray32f_image_t img_out(15, 15); - std::vector> val_in - { + std::vector> val_in{ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - {255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, - {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, - {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, - {255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, - {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} - }; - - std::vector> val_exp1 - { - {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f}, - { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - { 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, - { 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, - { 1, 0, 1, 2, 1.4f, 1, 1, 1, 1, 1, 1.4f, 2, 1, 0, 1}, - { 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, - { 1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, - { 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1}, - { 1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, - { 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, - { 1, 0, 1, 2, 1.4f, 1, 1, 1, 1, 1, 1.4f, 2, 1, 0, 1}, - { 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, - { 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, - { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f} - }; - - std::vector> val_exp2 - { + {255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255}, + {255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}; + + std::vector> val_exp1{ + {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + {1, 0, 1, 2, 1.4f, 1, 1, 1, 1, 1, 1.4f, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 1}, + {1, 0, 1, 2, 1.4f, 1, 1, 1, 1, 1, 1.4f, 2, 1, 0, 1}, + {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1.4f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.4f}}; + + std::vector> val_exp2{ {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, @@ -212,20 +212,26 @@ void test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance {1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1}, {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2} - }; + {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}}; fill_image_view(val_in, view(img_in)); fill_image_view(val_exp1, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::euclidean_approximation, gil::mask_size::five); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::five); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); fill_image_view(val_exp2, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::manhattan, gil::mask_size::five); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::manhattan, + gil::mask_size::five); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); - } void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() @@ -234,278 +240,773 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() gil::gray32f_image_t img_expected(15, 15); gil::gray32f_image_t img_out(15, 15); - std::vector> val_in - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0} - }; - - std::vector> val_exp_precise - { - {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 4.1231055f, 3.1622777f, - 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, - { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4, - 3, 2, 1, 0, 1, 2, 3, 4}, - { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 3.1622777f, - 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f}, - { 2, 1, 0, 1, 2, 3, 4, 3.6055512f, - 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f}, - { 2.236068f, 1.4142135f, 1, 1.4142135f, 2.236068f, 3.1622777f, 4.1231055f, 4.2426405f, - 3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5}, - { 2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4.472136f, 5, - 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f}, - {3.6055512f, 3.1622777f, 3, 3.1622777f, 3.6055512f, 4.2426405f, 5, 5.8309517f, - 5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f}, - { 4.472136f, 4.1231055f, 4, 4.1231055f, 4.472136f, 5, 5.656854f, 6.4031243f, - 6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.0000005f}, - {5.3851647f, 5.0990195f, 5, 5.0990195f, 5.3851647f, 5.8309517f, 6.4031243f, 7.0710683f, - 7.2801094f, 7.0710683f, 7.0000005f, 6.7082043f, 6.3245554f, 6.0827622f, 6}, - {6.3245554f, 6.0827622f, 6, 6.0827622f, 6.3245554f, 6.7082043f, 7.2111025f, 7.8102503f, - 7.8102503f, 7.0710683f, 6.4031243f, 5.8309517f, 5.3851647f, 5.0990195f, 5}, - {7.2801094f, 7.0710683f, 7.0000005f, 7.0710683f, 7.2801094f, 7.6157737f, 8.062258f, 8.062258f, - 7.2111025f, 6.4031243f, 5.656854f, 5, 4.472136f, 4.1231055f, 4}, - { 8.246211f, 8.062258f, 8, 8.062258f, 8.246211f, 8.5440035f, 8.5440035f, 7.6157737f, - 6.7082043f, 5.8309517f, 5, 4.2426405f, 3.6055512f, 3.1622777f, 3}, - { 9.219544f, 9.055386f, 9, 9.055386f, 9.219544f, 9.219544f, 8.246211f, 7.2801094f, - 6.3245554f, 5.3851647f, 4.472136f, 3.6055512f, 2.828427f, 2.236068f, 2}, - {10.198039f, 10.049875f, 10, 10.049875f, 10.049875f, 9.055386f, 8.062258f, 7.0710683f, - 6.0827622f, 5.0990195f, 4.1231055f, 3.1622777f, 2.236068f, 1.4142135f, 1}, - {11.180341f, 11.045361f, 11, 11, 10, 9, 8, 7, - 6, 5, 4, 3, 2, 1, 0} - }; + std::vector> val_in{ + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0}}; + + std::vector> val_exp_precise{ + {3.6055512f, + 3.1622777f, + 3, + 3.1622777f, + 3.6055512f, + 4.2426405f, + 4.1231055f, + 3.1622777f, + 2.236068f, + 1.4142135f, + 1, + 1.4142135f, + 2.236068f, + 3.1622777f, + 4.1231055f}, + {2.828427f, 2.236068f, 2, 2.236068f, 2.828427f, 3.6055512f, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2.236068f, + 1.4142135f, + 1, + 1.4142135f, + 2.236068f, + 3.1622777f, + 4.1231055f, + 3.1622777f, + 2.236068f, + 1.4142135f, + 1, + 1.4142135f, + 2.236068f, + 3.1622777f, + 4.1231055f}, + {2, + 1, + 0, + 1, + 2, + 3, + 4, + 3.6055512f, + 2.828427f, + 2.236068f, + 2, + 2.236068f, + 2.828427f, + 3.6055512f, + 4.472136f}, + {2.236068f, + 1.4142135f, + 1, + 1.4142135f, + 2.236068f, + 3.1622777f, + 4.1231055f, + 4.2426405f, + 3.6055512f, + 3.1622777f, + 3, + 3.1622777f, + 3.6055512f, + 4.2426405f, + 5}, + {2.828427f, + 2.236068f, + 2, + 2.236068f, + 2.828427f, + 3.6055512f, + 4.472136f, + 5, + 4.472136f, + 4.1231055f, + 4, + 4.1231055f, + 4.472136f, + 5, + 5.656854f}, + {3.6055512f, + 3.1622777f, + 3, + 3.1622777f, + 3.6055512f, + 4.2426405f, + 5, + 5.8309517f, + 5.3851647f, + 5.0990195f, + 5, + 5.0990195f, + 5.3851647f, + 5.8309517f, + 6.4031243f}, + {4.472136f, + 4.1231055f, + 4, + 4.1231055f, + 4.472136f, + 5, + 5.656854f, + 6.4031243f, + 6.3245554f, + 6.0827622f, + 6, + 6.0827622f, + 6.3245554f, + 6.7082043f, + 7.0000005f}, + {5.3851647f, + 5.0990195f, + 5, + 5.0990195f, + 5.3851647f, + 5.8309517f, + 6.4031243f, + 7.0710683f, + 7.2801094f, + 7.0710683f, + 7.0000005f, + 6.7082043f, + 6.3245554f, + 6.0827622f, + 6}, + {6.3245554f, + 6.0827622f, + 6, + 6.0827622f, + 6.3245554f, + 6.7082043f, + 7.2111025f, + 7.8102503f, + 7.8102503f, + 7.0710683f, + 6.4031243f, + 5.8309517f, + 5.3851647f, + 5.0990195f, + 5}, + {7.2801094f, + 7.0710683f, + 7.0000005f, + 7.0710683f, + 7.2801094f, + 7.6157737f, + 8.062258f, + 8.062258f, + 7.2111025f, + 6.4031243f, + 5.656854f, + 5, + 4.472136f, + 4.1231055f, + 4}, + {8.246211f, + 8.062258f, + 8, + 8.062258f, + 8.246211f, + 8.5440035f, + 8.5440035f, + 7.6157737f, + 6.7082043f, + 5.8309517f, + 5, + 4.2426405f, + 3.6055512f, + 3.1622777f, + 3}, + {9.219544f, + 9.055386f, + 9, + 9.055386f, + 9.219544f, + 9.219544f, + 8.246211f, + 7.2801094f, + 6.3245554f, + 5.3851647f, + 4.472136f, + 3.6055512f, + 2.828427f, + 2.236068f, + 2}, + {10.198039f, + 10.049875f, + 10, + 10.049875f, + 10.049875f, + 9.055386f, + 8.062258f, + 7.0710683f, + 6.0827622f, + 5.0990195f, + 4.1231055f, + 3.1622777f, + 2.236068f, + 1.4142135f, + 1}, + {11.180341f, 11.045361f, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; fill_image_view(val_in, view(img_in)); fill_image_view(val_exp_precise, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::precise_euclidean, gil::mask_size::not_applicable); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); check_for_negligible_varition(view(img_out), view(img_expected)); - std::vector> val_exp_euclidean_three - { - { 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, 3.6935883f, 4.1078796f, - 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, - 2.324295f, 3.2792969f, 4.2342987f}, - { 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, 2.7385864f, 3.6935883f, - 3.8200073f, 2.8650055f, 1.9100037f, 0.95500183f, 0, 0.95500183f, - 1.9100037f, 2.8650055f, 3.8200073f}, - { 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, - 4.2342987f, 3.2792969f, 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, - 2.324295f, 3.2792969f, 4.2342987f}, - { 1.9100037f, 0.95500183f, 0, 0.95500183f, 1.9100037f, 2.8650055f, - 3.8200073f, 3.6935883f, 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, - 2.7385864f, 3.6935883f, 4.64859f}, - { 2.324295f, 1.3692932f, 0.95500183f, 1.3692932f, 2.324295f, 3.2792969f, - 4.2342987f, 4.1078796f, 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, - 3.6935883f, 4.1078796f, 5.0628815f}, - { 2.7385864f, 2.324295f, 1.9100037f, 2.324295f, 2.7385864f, 3.6935883f, - 4.64859f, 5.0628815f, 4.64859f, 4.2342987f, 3.8200073f, 4.2342987f, - 4.64859f, 5.0628815f, 5.477173f}, - { 3.6935883f, 3.2792969f, 2.8650055f, 3.2792969f, 3.6935883f, 4.1078796f, - 5.0628815f, 6.0178833f, 5.603592f, 5.1893005f, 4.775009f, 5.1893005f, - 5.603592f, 6.0178833f, 6.4321747f}, - { 4.64859f, 4.2342987f, 3.8200073f, 4.2342987f, 4.64859f, 5.0628815f, - 5.477173f, 6.4321747f, 6.5585938f, 6.1443024f, 5.730011f, 6.1443024f, - 6.5585938f, 6.972885f, 6.685013f}, - { 5.603592f, 5.1893005f, 4.775009f, 5.1893005f, 5.603592f, 6.0178833f, - 6.4321747f, 6.846466f, 7.5135956f, 7.099304f, 6.685013f, 6.972885f, - 6.5585938f, 6.1443024f, 5.730011f}, - { 6.5585938f, 6.1443024f, 5.730011f, 6.1443024f, 6.5585938f, 6.972885f, - 7.3871765f, 7.801468f, 7.801468f, 6.846466f, 6.4321747f, 6.0178833f, - 5.603592f, 5.1893005f, 4.775009f}, - { 7.5135956f, 7.099304f, 6.685013f, 7.099304f, 7.5135956f, 7.927887f, - 8.342178f, 8.342178f, 7.3871765f, 6.4321747f, 5.477173f, 5.0628815f, - 4.64859f, 4.2342987f, 3.8200073f}, - { 8.468597f, 8.054306f, 7.6400146f, 8.054306f, 8.468597f, 8.882889f, - 8.882889f, 7.927887f, 6.972885f, 6.0178833f, 5.0628815f, 4.1078796f, - 3.6935883f, 3.2792969f, 2.8650055f}, - { 9.423599f, 9.009308f, 8.5950165f, 9.009308f, 9.423599f, 9.423599f, - 8.468597f, 7.5135956f, 6.5585938f, 5.603592f, 4.64859f, 3.6935883f, - 2.7385864f, 2.324295f, 1.9100037f}, - { 10.378601f, 9.96431f, 9.550018f, 9.96431f, 9.96431f, 9.009308f, - 8.054306f, 7.099304f, 6.1443024f, 5.1893005f, 4.2342987f, 3.2792969f, - 2.324295f, 1.3692932f, 0.95500183f}, - { 11.333603f, 10.919312f, 10.50502f, 10.50502f, 9.550018f, 8.5950165f, - 7.6400146f, 6.685013f, 5.730011f, 4.775009f, 3.8200073f, 2.8650055f, - 1.9100037f, 0.95500183f, 0.f} - }; + std::vector> val_exp_euclidean_three{ + {3.6935883f, + 3.2792969f, + 2.8650055f, + 3.2792969f, + 3.6935883f, + 4.1078796f, + 4.2342987f, + 3.2792969f, + 2.324295f, + 1.3692932f, + 0.95500183f, + 1.3692932f, + 2.324295f, + 3.2792969f, + 4.2342987f}, + {2.7385864f, + 2.324295f, + 1.9100037f, + 2.324295f, + 2.7385864f, + 3.6935883f, + 3.8200073f, + 2.8650055f, + 1.9100037f, + 0.95500183f, + 0, + 0.95500183f, + 1.9100037f, + 2.8650055f, + 3.8200073f}, + {2.324295f, + 1.3692932f, + 0.95500183f, + 1.3692932f, + 2.324295f, + 3.2792969f, + 4.2342987f, + 3.2792969f, + 2.324295f, + 1.3692932f, + 0.95500183f, + 1.3692932f, + 2.324295f, + 3.2792969f, + 4.2342987f}, + {1.9100037f, + 0.95500183f, + 0, + 0.95500183f, + 1.9100037f, + 2.8650055f, + 3.8200073f, + 3.6935883f, + 2.7385864f, + 2.324295f, + 1.9100037f, + 2.324295f, + 2.7385864f, + 3.6935883f, + 4.64859f}, + {2.324295f, + 1.3692932f, + 0.95500183f, + 1.3692932f, + 2.324295f, + 3.2792969f, + 4.2342987f, + 4.1078796f, + 3.6935883f, + 3.2792969f, + 2.8650055f, + 3.2792969f, + 3.6935883f, + 4.1078796f, + 5.0628815f}, + {2.7385864f, + 2.324295f, + 1.9100037f, + 2.324295f, + 2.7385864f, + 3.6935883f, + 4.64859f, + 5.0628815f, + 4.64859f, + 4.2342987f, + 3.8200073f, + 4.2342987f, + 4.64859f, + 5.0628815f, + 5.477173f}, + {3.6935883f, + 3.2792969f, + 2.8650055f, + 3.2792969f, + 3.6935883f, + 4.1078796f, + 5.0628815f, + 6.0178833f, + 5.603592f, + 5.1893005f, + 4.775009f, + 5.1893005f, + 5.603592f, + 6.0178833f, + 6.4321747f}, + {4.64859f, + 4.2342987f, + 3.8200073f, + 4.2342987f, + 4.64859f, + 5.0628815f, + 5.477173f, + 6.4321747f, + 6.5585938f, + 6.1443024f, + 5.730011f, + 6.1443024f, + 6.5585938f, + 6.972885f, + 6.685013f}, + {5.603592f, + 5.1893005f, + 4.775009f, + 5.1893005f, + 5.603592f, + 6.0178833f, + 6.4321747f, + 6.846466f, + 7.5135956f, + 7.099304f, + 6.685013f, + 6.972885f, + 6.5585938f, + 6.1443024f, + 5.730011f}, + {6.5585938f, + 6.1443024f, + 5.730011f, + 6.1443024f, + 6.5585938f, + 6.972885f, + 7.3871765f, + 7.801468f, + 7.801468f, + 6.846466f, + 6.4321747f, + 6.0178833f, + 5.603592f, + 5.1893005f, + 4.775009f}, + {7.5135956f, + 7.099304f, + 6.685013f, + 7.099304f, + 7.5135956f, + 7.927887f, + 8.342178f, + 8.342178f, + 7.3871765f, + 6.4321747f, + 5.477173f, + 5.0628815f, + 4.64859f, + 4.2342987f, + 3.8200073f}, + {8.468597f, + 8.054306f, + 7.6400146f, + 8.054306f, + 8.468597f, + 8.882889f, + 8.882889f, + 7.927887f, + 6.972885f, + 6.0178833f, + 5.0628815f, + 4.1078796f, + 3.6935883f, + 3.2792969f, + 2.8650055f}, + {9.423599f, + 9.009308f, + 8.5950165f, + 9.009308f, + 9.423599f, + 9.423599f, + 8.468597f, + 7.5135956f, + 6.5585938f, + 5.603592f, + 4.64859f, + 3.6935883f, + 2.7385864f, + 2.324295f, + 1.9100037f}, + {10.378601f, + 9.96431f, + 9.550018f, + 9.96431f, + 9.96431f, + 9.009308f, + 8.054306f, + 7.099304f, + 6.1443024f, + 5.1893005f, + 4.2342987f, + 3.2792969f, + 2.324295f, + 1.3692932f, + 0.95500183f}, + {11.333603f, + 10.919312f, + 10.50502f, + 10.50502f, + 9.550018f, + 8.5950165f, + 7.6400146f, + 6.685013f, + 5.730011f, + 4.775009f, + 3.8200073f, + 2.8650055f, + 1.9100037f, + 0.95500183f, + 0.f}}; fill_image_view(val_in, view(img_in)); fill_image_view(val_exp_euclidean_three, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::euclidean_approximation, gil::mask_size::three); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::three); check_for_negligible_varition(view(img_out), view(img_expected)); - std::vector> val_exp_euclidean_five - { - { 3.5969f, 3.1969f, 3, 3.1969f, 3.5969f, 4.2f, - 4.1969f, 3.1969f, 2.1969f, 1.4f, 1, 1.4f, - 2.1969f, 3.1969f, 4.1969f}, - { 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, - 4, 3, 2, 1, 0, 1, - 2, 3, 4}, - { 2.1969f, 1.4f, 1, 1.4f, 2.1969f, 3.1969f, - 4.1969f, 3.1969f, 2.1969f, 1.4f, 1, 1.4f, - 2.1969f, 3.1969f, 4.1969f}, - { 2, 1, 0, 1, 2, 3, - 4, 3.5969f, 2.8f, 2.1969f, 2, 2.1969f, - 2.8f, 3.5969f, 4.3938f}, - { 2.1969f, 1.4f, 1, 1.4f, 2.1969f, 3.1969f, - 4.1969f, 4.2f, 3.5969f, 3.1969f, 3, 3.1969f, - 3.5969f, 4.2f, 4.9968996f}, - { 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, - 4.3938f, 4.9968996f, 4.3938f, 4.1969f, 4, 4.1969f, - 4.3938f, 4.9968996f, 5.6f}, - { 3.5969f, 3.1969f, 3, 3.1969f, 3.5969f, 4.2f, - 4.9968996f, 5.7938f, 5.3938f, 5.1969f, 5, 5.1969f, - 5.3938f, 5.7938f, 6.3968997f}, - { 4.3938f, 4.1969f, 4, 4.1969f, 4.3938f, 4.9968996f, - 5.6f, 6.3968997f, 6.3938f, 6.1969f, 6, 6.1969f, - 6.3938f, 6.5906997f, 7}, - { 5.3938f, 5.1969f, 5, 5.1969f, 5.3938f, 5.7938f, - 6.3968997f, 7, 7.3938f, 7.1969f, 7, 6.5906997f, - 6.3938f, 6.1969f, 6}, - { 6.3938f, 6.1969f, 6, 6.1969f, 6.3938f, 6.5906997f, - 7.1937995f, 7.7969f, 7.7969f, 7, 6.3968997f, 5.7938f, - 5.3938f, 5.1969f, 5}, - { 7.3938f, 7.1969f, 7, 7.1969f, 7.3938f, 7.5906997f, - 7.9907f, 7.9907f, 7.1937995f, 6.3968997f, 5.6f, 4.9968996f, - 4.3938f, 4.1969f, 4}, - { 8.3938f, 8.196899f, 8, 8.196899f, 8.3938f, 8.5907f, - 8.5907f, 7.5906997f, 6.5906997f, 5.7938f, 4.9968996f, 4.2f, - 3.5969f, 3.1969f, 3}, - { 9.3938f, 9.196899f, 9, 9.196899f, 9.3938f, 9.3938f, - 8.3938f, 7.3938f, 6.3938f, 5.3938f, 4.3938f, 3.5969f, - 2.8f, 2.1969f, 2}, - {10.393799f, 10.196899f, 10, 10.196899f, 10.196899f, 9.196899f, - 8.196899f, 7.1969f, 6.1969f, 5.1969f, 4.1969f, 3.1969f, - 2.1969f, 1.4f, 1}, - {11.393799f, 11.196899f, 11, 11, 10, 9, - 8, 7, 6, 5, 4, 3, - 2, 1, 0} - }; + std::vector> val_exp_euclidean_five{ + {3.5969f, + 3.1969f, + 3, + 3.1969f, + 3.5969f, + 4.2f, + 4.1969f, + 3.1969f, + 2.1969f, + 1.4f, + 1, + 1.4f, + 2.1969f, + 3.1969f, + 4.1969f}, + {2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2.1969f, + 1.4f, + 1, + 1.4f, + 2.1969f, + 3.1969f, + 4.1969f, + 3.1969f, + 2.1969f, + 1.4f, + 1, + 1.4f, + 2.1969f, + 3.1969f, + 4.1969f}, + {2, 1, 0, 1, 2, 3, 4, 3.5969f, 2.8f, 2.1969f, 2, 2.1969f, 2.8f, 3.5969f, 4.3938f}, + {2.1969f, + 1.4f, + 1, + 1.4f, + 2.1969f, + 3.1969f, + 4.1969f, + 4.2f, + 3.5969f, + 3.1969f, + 3, + 3.1969f, + 3.5969f, + 4.2f, + 4.9968996f}, + {2.8f, + 2.1969f, + 2, + 2.1969f, + 2.8f, + 3.5969f, + 4.3938f, + 4.9968996f, + 4.3938f, + 4.1969f, + 4, + 4.1969f, + 4.3938f, + 4.9968996f, + 5.6f}, + {3.5969f, + 3.1969f, + 3, + 3.1969f, + 3.5969f, + 4.2f, + 4.9968996f, + 5.7938f, + 5.3938f, + 5.1969f, + 5, + 5.1969f, + 5.3938f, + 5.7938f, + 6.3968997f}, + {4.3938f, + 4.1969f, + 4, + 4.1969f, + 4.3938f, + 4.9968996f, + 5.6f, + 6.3968997f, + 6.3938f, + 6.1969f, + 6, + 6.1969f, + 6.3938f, + 6.5906997f, + 7}, + {5.3938f, + 5.1969f, + 5, + 5.1969f, + 5.3938f, + 5.7938f, + 6.3968997f, + 7, + 7.3938f, + 7.1969f, + 7, + 6.5906997f, + 6.3938f, + 6.1969f, + 6}, + {6.3938f, + 6.1969f, + 6, + 6.1969f, + 6.3938f, + 6.5906997f, + 7.1937995f, + 7.7969f, + 7.7969f, + 7, + 6.3968997f, + 5.7938f, + 5.3938f, + 5.1969f, + 5}, + {7.3938f, + 7.1969f, + 7, + 7.1969f, + 7.3938f, + 7.5906997f, + 7.9907f, + 7.9907f, + 7.1937995f, + 6.3968997f, + 5.6f, + 4.9968996f, + 4.3938f, + 4.1969f, + 4}, + {8.3938f, + 8.196899f, + 8, + 8.196899f, + 8.3938f, + 8.5907f, + 8.5907f, + 7.5906997f, + 6.5906997f, + 5.7938f, + 4.9968996f, + 4.2f, + 3.5969f, + 3.1969f, + 3}, + {9.3938f, + 9.196899f, + 9, + 9.196899f, + 9.3938f, + 9.3938f, + 8.3938f, + 7.3938f, + 6.3938f, + 5.3938f, + 4.3938f, + 3.5969f, + 2.8f, + 2.1969f, + 2}, + {10.393799f, + 10.196899f, + 10, + 10.196899f, + 10.196899f, + 9.196899f, + 8.196899f, + 7.1969f, + 6.1969f, + 5.1969f, + 4.1969f, + 3.1969f, + 2.1969f, + 1.4f, + 1}, + {11.393799f, 11.196899f, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; fill_image_view(val_in, view(img_in)); fill_image_view(val_exp_euclidean_five, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::euclidean_approximation, gil::mask_size::five); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::five); check_for_negligible_varition(view(img_out), view(img_expected)); - std::vector> val_exp_manhatten_three - { - { 5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, - { 4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, - { 3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, - { 2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, - { 3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, - { 4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, - { 5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, - { 6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, - { 7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, - { 8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, - { 9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, - {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, - {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, - {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, - {13, 12, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} - }; + std::vector> val_exp_manhatten_three{ + {5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, + {3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, + {4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, + {5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, + {6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, + {7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, + {8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, + {9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, + {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, + {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, + {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, + {13, 12, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; fill_image_view(val_exp_manhatten_three, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::manhattan, gil::mask_size::three); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::manhattan, + gil::mask_size::three); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); - - std::vector> val_exp_manhatten_five - { - { 5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, - { 4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, - { 3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, - { 2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, - { 3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, - { 4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, - { 5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, - { 6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, - { 7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, - { 8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, - { 9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, - {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, - {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, - {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, - {13, 12, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} - }; + std::vector> val_exp_manhatten_five{ + {5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {4, 3, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5}, + {2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6}, + {3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 7}, + {4, 3, 2, 3, 4, 5, 6, 7, 6, 5, 4, 5, 6, 7, 8}, + {5, 4, 3, 4, 5, 6, 7, 8, 7, 6, 5, 6, 7, 8, 8}, + {6, 5, 4, 5, 6, 7, 8, 9, 8, 7, 6, 7, 8, 8, 7}, + {7, 6, 5, 6, 7, 8, 9, 10, 9, 8, 7, 8, 8, 7, 6}, + {8, 7, 6, 7, 8, 9, 10, 11, 10, 9, 8, 8, 7, 6, 5}, + {9, 8, 7, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4}, + {10, 9, 8, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3}, + {11, 10, 9, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}, + {12, 11, 10, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, + {13, 12, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; fill_image_view(val_exp_manhatten_five, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::manhattan, gil::mask_size::five); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::manhattan, + gil::mask_size::five); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); - std::vector> val_exp_chessboard_three - { - { 3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, - { 2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, - { 2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, - { 2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, - { 2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, - { 2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, - { 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, - { 4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, - { 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, - { 7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, - { 8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, - { 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, - {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, - {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} - }; - + std::vector> val_exp_chessboard_three{ + {3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + {3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, + {4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, + {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, + {6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, + {7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, + {8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, + {9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, + {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, + {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; + fill_image_view(val_exp_chessboard_three, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::chessboard, gil::mask_size::three); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::chessboard, + gil::mask_size::three); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); - std::vector> val_exp_chessboard_five - { - {3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, - {2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, - {2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, - {2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, - {2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, - {2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, - {3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, - {4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, - {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, - {6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, - {7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, - {8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, - {9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, - {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, - {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} - }; - + std::vector> val_exp_chessboard_five{ + {3, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 2, 1, 1, 1, 2, 3, 4}, + {2, 1, 0, 1, 2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 4}, + {2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4}, + {2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + {3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5}, + {4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6}, + {5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}, + {6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5}, + {7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4}, + {8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3}, + {9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 3, 2, 2, 2}, + {10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, + {11, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}; + fill_image_view(val_exp_chessboard_five, view(img_expected)); - gil::distance_transform(view(img_in), view(img_out), gil::distance_from::off_pixels, - gil::distance_type::chessboard, gil::mask_size::five); + gil::distance_transform( + view(img_in), + view(img_out), + gil::distance_from::off_pixels, + gil::distance_type::chessboard, + gil::mask_size::five); BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); - } int main() From 5ce9ca91c78be71e777cbf7b34fc7e5abf8a4757 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 2 Sep 2026 22:40:38 +0200 Subject: [PATCH 18/37] Add regression test for 'nothing to find' case --- .../image_processing/distance_transform.hpp | 13 +++++- .../image_processing/distance_transform.cpp | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/include/boost/gil/image_processing/distance_transform.hpp b/include/boost/gil/image_processing/distance_transform.hpp index 96d22e3929..2d36c4fe28 100644 --- a/include/boost/gil/image_processing/distance_transform.hpp +++ b/include/boost/gil/image_processing/distance_transform.hpp @@ -492,8 +492,6 @@ void distance_transorm_precise_impl( { intermediate_row_itr[x][0] = one_dimensional_sq_euclidean_dt_along_row[x]; - float const distance_transform = std::sqrt(intermediate_row_itr[x][0]); - float constexpr dst_channel_max = (is_same::value) ? dt_infinite @@ -506,6 +504,17 @@ void distance_transorm_precise_impl( : static_cast( (std::numeric_limits::type>::min)()); + // A squared distance at or beyond dt_infinite means no target pixel was + // reachable (e.g. no 'on' pixel exists when dist_from == on_pixels). + // Report the clamped sentinel directly instead of sqrt(dt_infinite): + // taking the square root first would collapse it down to a small, + // misleadingly finite value (~31623 for dt_infinite == 1e9) that + // would then slip straight past the dst_channel_max clamp below. + float const squared_distance = intermediate_row_itr[x][0]; + float const distance_transform = (squared_distance >= dt_infinite) + ? dst_channel_max + : std::sqrt(squared_distance); + dst_view(x, y)[0] = static_cast::type>( (distance_transform > dst_channel_max) ? dst_channel_max diff --git a/test/core/image_processing/distance_transform.cpp b/test/core/image_processing/distance_transform.cpp index effe58fe1d..fc71d2d8f8 100644 --- a/test/core/image_processing/distance_transform.cpp +++ b/test/core/image_processing/distance_transform.cpp @@ -1009,11 +1009,53 @@ void test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels() BOOST_TEST(equal_pixels(const_view(img_out), const_view(img_expected))); } +void test_precise_no_reachable_target_reports_clamped_sentinel() +{ + // When no pixel of the requested kind exists (here: no 'on' pixel, while + // asking for the distance to the nearest 'on' pixel), every output pixel + // is unreachable and must report the same clamped sentinel value that + // the mask-3/mask-5 chamfer methods report for the identical input. + // + // The precise (Felzenszwalt-Huttenlocher) method computes everything in + // squared-distance space and only takes a final sqrt(): naively + // sqrt()-ing the internal infinity sentinel collapses dt_infinite + // (1e9) down to a small, misleadingly finite ~31623, which would then + // slip straight past the dst_channel_max clamp instead of being + // recognized as "unreachable". + gil::gray8_image_t img_in(5, 5); + gil::gray32f_image_t img_out_precise(5, 5); + gil::gray32f_image_t img_out_chamfer(5, 5); + + gil::fill_pixels(view(img_in), gil::gray8_pixel_t(0)); // no 'on' pixel anywhere + + gil::distance_transform( + const_view(img_in), + view(img_out_precise), + gil::distance_from::on_pixels, + gil::distance_type::precise_euclidean, + gil::mask_size::not_applicable); + + gil::distance_transform( + const_view(img_in), + view(img_out_chamfer), + gil::distance_from::on_pixels, + gil::distance_type::euclidean_approximation, + gil::mask_size::three); + + // Both must agree on the clamped sentinel for every pixel. + BOOST_TEST(equal_pixels(const_view(img_out_precise), const_view(img_out_chamfer))); + + for (std::ptrdiff_t y = 0; y < img_out_precise.height(); ++y) + for (std::ptrdiff_t x = 0; x < img_out_precise.width(); ++x) + BOOST_TEST_EQ(view(img_out_precise)(x, y)[0], view(img_out_chamfer)(x, y)[0]); +} + int main() { test_manhattan_uint8_t_input_uint8_t_output_distance_from_off_pixels(); test_chessboard_uint16_t_input_uint8_t_output_distance_from_on_pixels(); test_euclidean_approx_and_manhattan_uint8_t_input_float32_t_output_distance_from_off_pixels(); test_all_uint8_t_input_float32_t_ouptut_distance_from_off_pixels(); + test_precise_no_reachable_target_reports_clamped_sentinel(); return ::boost::report_errors(); } From b19a096de4a0ace050c822408e86fb763ea2f53f Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Tue, 1 Sep 2026 21:57:07 +0200 Subject: [PATCH 19/37] docs: migrate from Sphinx/RST/Doxygen to Antora/AsciiDoc Convert all prose documentation under doc/ to AsciiDoc pages built with Antora, following the boostorg/redis and boostorg/url convention (antora.yml, nav.adoc, build_antora.sh, package.json). Fix two pre-existing navigation gaps in the process: the contrast_enhancement pages that were unreachable from the old toctree, and the duplicated histogram/extension inclusion. The C++ API reference stays Doxygen-generated for now and is linked in via modules/ROOT/pages/reference.adoc: MrDocs 0.8.0 segfaults on most of GIL's CRTP-based pixel/image/iterator/locator headers, so a MrDocs-native reference (mrdocs.yml is prepared but not wired into antora.yml) is blocked on an upstream fix. Update the docs CI actions and pixi.toml's new `docs` environment (Node.js + Doxygen) accordingly, and remove the old Sphinx toolchain files. Co-Authored-By: Claude Sonnet 5 --- .github/actions/docs-prerequisites/action.yml | 5 +- .github/actions/generate-doc/action.yml | 9 +- .github/actions/generate-doc/docs-config.sh | 14 +- .gitignore | 3 +- doc/Jamfile | 57 +- doc/Makefile | 133 -- doc/README.md | 36 +- doc/_static/boost-gil.png | Bin 17091 -> 0 bytes doc/_static/boost-gil.svg | 191 -- doc/_static/boost.css | 735 ------- doc/_static/boost.png | Bin 6308 -> 0 bytes doc/_static/doxygen.css | 5 - doc/_static/gil.svg | 186 -- doc/_static/home.png | Bin 358 -> 0 bytes doc/_static/logo.png | Bin 38344 -> 0 bytes doc/_static/next.png | Bin 336 -> 0 bytes doc/_static/prev.png | Bin 334 -> 0 bytes doc/_static/style.css | 44 - doc/_static/up.png | Bin 370 -> 0 bytes doc/_templates/layout.html | 139 -- doc/antora.yml | 22 + doc/conf.py | 225 -- doc/design/channel.rst | 204 -- doc/design/color_base.rst | 245 --- doc/design/color_space.rst | 89 - doc/design/concepts.rst | 70 - doc/design/dynamic_image.rst | 211 -- doc/design/examples.rst | 209 -- doc/design/extending.rst | 166 -- doc/design/image.rst | 103 - doc/design/image_view.rst | 433 ---- doc/design/index.rst | 27 - doc/design/metafunctions.rst | 242 --- doc/design/pixel.rst | 285 --- doc/design/pixel_iterator.rst | 294 --- doc/design/pixel_locator.rst | 303 --- doc/design/point.rst | 56 - doc/design/technicalities.rst | 89 - doc/histogram/create.rst | 42 - doc/histogram/extend.rst | 68 - 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 - .../affine-region-detectors.rst | 115 - .../histogram_equalization.rst | 100 - .../histogram_matching.rst | 87 - doc/image_processing/index.rst | 15 - doc/images/gil.png | Bin 21385 -> 0 bytes doc/index.html | 9 - doc/index.rst | 67 - doc/installation.rst | 32 - doc/io.rst | 654 ------ doc/local-playbook.yml | 40 + .../ROOT/images}/Moravec-window-corner.png | Bin .../ROOT/images}/Moravec-window-edge.png | Bin doc/{_static => modules/ROOT/images}/gil.png | Bin doc/modules/ROOT/images/he_chart.png | Bin 0 -> 14693 bytes doc/{ => modules/ROOT}/images/interleaved.jpg | Bin doc/{ => modules/ROOT}/images/mandel.jpg | Bin .../ROOT}/images/monkey_steps.jpg | Bin .../ROOT}/images/pixel_locator.gif | Bin doc/{ => modules/ROOT}/images/planar.jpg | Bin doc/modules/ROOT/nav.adoc | 57 + .../ROOT/pages/design/basics.adoc} | 15 +- doc/modules/ROOT/pages/design/channel.adoc | 209 ++ doc/modules/ROOT/pages/design/color_base.adoc | 242 +++ .../ROOT/pages/design/color_space.adoc | 88 + doc/modules/ROOT/pages/design/concepts.adoc | 71 + .../ROOT/pages/design/conclusions.adoc} | 22 +- .../ROOT/pages/design/dynamic_image.adoc | 215 ++ doc/modules/ROOT/pages/design/examples.adoc | 209 ++ doc/modules/ROOT/pages/design/extending.adoc | 162 ++ doc/modules/ROOT/pages/design/image.adoc | 103 + doc/modules/ROOT/pages/design/image_view.adoc | 437 ++++ doc/modules/ROOT/pages/design/index.adoc | 4 + .../ROOT/pages/design/metafunctions.adoc | 244 +++ doc/modules/ROOT/pages/design/pixel.adoc | 289 +++ .../ROOT/pages/design/pixel_iterator.adoc | 299 +++ .../ROOT/pages/design/pixel_locator.adoc | 306 +++ doc/modules/ROOT/pages/design/point.adoc | 54 + .../ROOT/pages/design/technicalities.adoc | 88 + doc/modules/ROOT/pages/examples.adoc | 26 + doc/modules/ROOT/pages/histogram/create.adoc | 43 + .../ROOT/pages/histogram/cumulative.adoc} | 34 +- doc/modules/ROOT/pages/histogram/extend.adoc | 62 + .../pages/histogram/extension/index.adoc} | 10 +- .../pages/histogram/extension/overview.adoc | 26 + .../ROOT/pages/histogram/extension/std.adoc | 36 + doc/modules/ROOT/pages/histogram/fill.adoc | 98 + doc/modules/ROOT/pages/histogram/index.adoc | 4 + .../ROOT/pages/histogram/limitations.adoc | 4 + .../ROOT/pages/histogram/overview.adoc | 22 + .../pages/histogram/stl_compatibility.adoc | 4 + .../ROOT/pages/histogram/subhistogram.adoc | 65 + .../ROOT/pages/histogram/utilities.adoc | 4 + .../affine-region-detectors.adoc | 103 + .../ROOT/pages/image_processing/basics.adoc} | 23 +- .../histogram_equalization.adoc | 76 + .../histogram_matching.adoc | 69 + .../contrast_enhancement/index.adoc} | 9 +- .../contrast_enhancement/overview.adoc} | 13 +- .../ROOT/pages/image_processing/index.adoc | 4 + .../pages/image_processing/overview.adoc} | 7 +- doc/modules/ROOT/pages/index.adoc | 8 + doc/modules/ROOT/pages/installation.adoc | 26 + doc/modules/ROOT/pages/io.adoc | 705 ++++++ doc/modules/ROOT/pages/naming.adoc | 50 + doc/modules/ROOT/pages/numeric.adoc | 5 + doc/modules/ROOT/pages/reference.adoc | 25 + .../ROOT/pages/toolbox.adoc} | 49 +- doc/modules/ROOT/pages/tutorial/gradient.adoc | 1019 +++++++++ .../ROOT/pages/tutorial/histogram.adoc | 100 + doc/modules/ROOT/pages/tutorial/video.adoc | 11 + doc/mrdocs.yml | 50 + doc/naming.rst | 48 - doc/numeric.rst | 7 - doc/package-lock.json | 1931 +++++++++++++++++ doc/package.json | 7 + doc/requirements.txt | 1 - doc/tutorial/gradient.rst | 1007 --------- doc/tutorial/histogram.rst | 103 - doc/tutorial/video.rst | 12 - 128 files changed, 7833 insertions(+), 7558 deletions(-) delete mode 100644 doc/Makefile delete mode 100644 doc/_static/boost-gil.png delete mode 100644 doc/_static/boost-gil.svg delete mode 100644 doc/_static/boost.css delete mode 100644 doc/_static/boost.png delete mode 100644 doc/_static/doxygen.css delete mode 100644 doc/_static/gil.svg delete mode 100644 doc/_static/home.png delete mode 100644 doc/_static/logo.png delete mode 100644 doc/_static/next.png delete mode 100644 doc/_static/prev.png delete mode 100644 doc/_static/style.css delete mode 100644 doc/_static/up.png delete mode 100644 doc/_templates/layout.html create mode 100644 doc/antora.yml delete mode 100644 doc/conf.py delete mode 100644 doc/design/channel.rst delete mode 100644 doc/design/color_base.rst delete mode 100644 doc/design/color_space.rst delete mode 100644 doc/design/concepts.rst delete mode 100644 doc/design/dynamic_image.rst delete mode 100644 doc/design/examples.rst delete mode 100644 doc/design/extending.rst delete mode 100644 doc/design/image.rst delete mode 100644 doc/design/image_view.rst delete mode 100644 doc/design/index.rst delete mode 100644 doc/design/metafunctions.rst delete mode 100644 doc/design/pixel.rst delete mode 100644 doc/design/pixel_iterator.rst delete mode 100644 doc/design/pixel_locator.rst delete mode 100644 doc/design/point.rst delete mode 100644 doc/design/technicalities.rst delete mode 100644 doc/histogram/create.rst delete mode 100644 doc/histogram/extend.rst delete mode 100644 doc/histogram/extension/overview.rst delete mode 100644 doc/histogram/extension/std.rst delete mode 100644 doc/histogram/fill.rst delete mode 100644 doc/histogram/index.rst delete mode 100644 doc/histogram/limitations.rst delete mode 100644 doc/histogram/overview.rst delete mode 100644 doc/histogram/stl_compatibility.rst delete mode 100644 doc/histogram/subhistogram.rst delete mode 100644 doc/histogram/utilities.rst delete mode 100644 doc/image_processing/affine-region-detectors.rst delete mode 100644 doc/image_processing/contrast_enhancement/histogram_equalization.rst delete mode 100644 doc/image_processing/contrast_enhancement/histogram_matching.rst delete mode 100644 doc/image_processing/index.rst delete mode 100644 doc/images/gil.png delete mode 100644 doc/index.html delete mode 100644 doc/index.rst delete mode 100644 doc/installation.rst delete mode 100644 doc/io.rst create mode 100644 doc/local-playbook.yml rename doc/{image_processing => modules/ROOT/images}/Moravec-window-corner.png (100%) rename doc/{image_processing => modules/ROOT/images}/Moravec-window-edge.png (100%) rename doc/{_static => modules/ROOT/images}/gil.png (100%) create mode 100644 doc/modules/ROOT/images/he_chart.png rename doc/{ => modules/ROOT}/images/interleaved.jpg (100%) rename doc/{ => modules/ROOT}/images/mandel.jpg (100%) rename doc/{ => modules/ROOT}/images/monkey_steps.jpg (100%) rename doc/{ => modules/ROOT}/images/pixel_locator.gif (100%) rename doc/{ => modules/ROOT}/images/planar.jpg (100%) create mode 100644 doc/modules/ROOT/nav.adoc rename doc/{design/basics.rst => modules/ROOT/pages/design/basics.adoc} (81%) create mode 100644 doc/modules/ROOT/pages/design/channel.adoc create mode 100644 doc/modules/ROOT/pages/design/color_base.adoc create mode 100644 doc/modules/ROOT/pages/design/color_space.adoc create mode 100644 doc/modules/ROOT/pages/design/concepts.adoc rename doc/{design/conclusions.rst => modules/ROOT/pages/design/conclusions.adoc} (84%) create mode 100644 doc/modules/ROOT/pages/design/dynamic_image.adoc create mode 100644 doc/modules/ROOT/pages/design/examples.adoc create mode 100644 doc/modules/ROOT/pages/design/extending.adoc create mode 100644 doc/modules/ROOT/pages/design/image.adoc create mode 100644 doc/modules/ROOT/pages/design/image_view.adoc create mode 100644 doc/modules/ROOT/pages/design/index.adoc create mode 100644 doc/modules/ROOT/pages/design/metafunctions.adoc create mode 100644 doc/modules/ROOT/pages/design/pixel.adoc create mode 100644 doc/modules/ROOT/pages/design/pixel_iterator.adoc create mode 100644 doc/modules/ROOT/pages/design/pixel_locator.adoc create mode 100644 doc/modules/ROOT/pages/design/point.adoc create mode 100644 doc/modules/ROOT/pages/design/technicalities.adoc create mode 100644 doc/modules/ROOT/pages/examples.adoc create mode 100644 doc/modules/ROOT/pages/histogram/create.adoc rename doc/{histogram/cumulative.rst => modules/ROOT/pages/histogram/cumulative.adoc} (55%) create mode 100644 doc/modules/ROOT/pages/histogram/extend.adoc rename doc/{histogram/extension/index.rst => modules/ROOT/pages/histogram/extension/index.adoc} (56%) create mode 100644 doc/modules/ROOT/pages/histogram/extension/overview.adoc create mode 100644 doc/modules/ROOT/pages/histogram/extension/std.adoc create mode 100644 doc/modules/ROOT/pages/histogram/fill.adoc create mode 100644 doc/modules/ROOT/pages/histogram/index.adoc create mode 100644 doc/modules/ROOT/pages/histogram/limitations.adoc create mode 100644 doc/modules/ROOT/pages/histogram/overview.adoc create mode 100644 doc/modules/ROOT/pages/histogram/stl_compatibility.adoc create mode 100644 doc/modules/ROOT/pages/histogram/subhistogram.adoc create mode 100644 doc/modules/ROOT/pages/histogram/utilities.adoc create mode 100644 doc/modules/ROOT/pages/image_processing/affine-region-detectors.adoc rename doc/{image_processing/basics.rst => modules/ROOT/pages/image_processing/basics.adoc} (87%) create mode 100644 doc/modules/ROOT/pages/image_processing/contrast_enhancement/histogram_equalization.adoc create mode 100644 doc/modules/ROOT/pages/image_processing/contrast_enhancement/histogram_matching.adoc rename doc/{image_processing/contrast_enhancement/index.rst => modules/ROOT/pages/image_processing/contrast_enhancement/index.adoc} (53%) rename doc/{image_processing/contrast_enhancement/overview.rst => modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc} (61%) create mode 100644 doc/modules/ROOT/pages/image_processing/index.adoc rename doc/{image_processing/overview.rst => modules/ROOT/pages/image_processing/overview.adoc} (53%) create mode 100644 doc/modules/ROOT/pages/index.adoc create mode 100644 doc/modules/ROOT/pages/installation.adoc create mode 100644 doc/modules/ROOT/pages/io.adoc create mode 100644 doc/modules/ROOT/pages/naming.adoc create mode 100644 doc/modules/ROOT/pages/numeric.adoc create mode 100644 doc/modules/ROOT/pages/reference.adoc rename doc/{toolbox.rst => modules/ROOT/pages/toolbox.adoc} (52%) create mode 100644 doc/modules/ROOT/pages/tutorial/gradient.adoc create mode 100644 doc/modules/ROOT/pages/tutorial/histogram.adoc create mode 100644 doc/modules/ROOT/pages/tutorial/video.adoc create mode 100644 doc/mrdocs.yml delete mode 100644 doc/naming.rst delete mode 100644 doc/numeric.rst create mode 100644 doc/package-lock.json create mode 100644 doc/package.json delete mode 100644 doc/requirements.txt delete mode 100644 doc/tutorial/gradient.rst delete mode 100644 doc/tutorial/histogram.rst delete mode 100644 doc/tutorial/video.rst diff --git a/.github/actions/docs-prerequisites/action.yml b/.github/actions/docs-prerequisites/action.yml index bc5a6a426c..58fdbd9535 100644 --- a/.github/actions/docs-prerequisites/action.yml +++ b/.github/actions/docs-prerequisites/action.yml @@ -3,5 +3,8 @@ 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 + - run: sudo apt-get install doxygen shell: bash + - uses: actions/setup-node@v4 + with: + node-version: 20 diff --git a/.github/actions/generate-doc/action.yml b/.github/actions/generate-doc/action.yml index 3ab8995f32..719aad8c0b 100644 --- a/.github/actions/generate-doc/action.yml +++ b/.github/actions/generate-doc/action.yml @@ -1,13 +1,12 @@ name: 'Generate Doc' -description: 'Runs b2 on lib/gil/doc to generate documentation' +description: 'Builds the Antora documentation for lib/gil/doc' runs: using: composite steps: - run: | - echo "using doxygen ;" > ~/user-config.jam - cd ../boost-root/libs - ../b2 gil/doc - cd gil + cd ../boost-root/libs/gil/doc + bash build_antora.sh + cd .. 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-doc/docs-config.sh b/.github/actions/generate-doc/docs-config.sh index 695d6d0eb0..1731c2dee0 100644 --- a/.github/actions/generate-doc/docs-config.sh +++ b/.github/actions/generate-doc/docs-config.sh @@ -15,18 +15,16 @@ git switch gh-pages if [ "${GITHUB_REF##*/}" = develop ]; then # Only updates develop directory and keeps others intact - rm -r develop + rm -rf develop mkdir -p develop/doc cp ../index.html develop/ - cp ../doc/index.html develop/doc - cp -a ../doc/html develop/doc/ + cp -a ../doc/build/site/. develop/doc/ else # main branch - rm index.html - rm -r html - cp ../doc/index.html . - cp -r ../doc/html . + rm -f index.html + rm -rf html gil + cp -a ../doc/build/site/. . fi # Remove version control -rm -rf .git \ No newline at end of file +rm -rf .git diff --git a/.gitignore b/.gitignore index 450dee9c5e..1d10f0eb9b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,10 @@ stdcerr /doc/*build* /io/output -# Documentation and Sphinx +# Documentation (Antora + Doxygen) doc/html doc/warnings.txt +doc/node_modules # Visual Studio (Code) /.vs diff --git a/doc/Jamfile b/doc/Jamfile index 2e35a53ecf..e11948448b 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -1,66 +1,21 @@ # Boost.GIL (Generic Image Library) - documentation # # Copyright (c) 2018 Stefan Seefeld +# Copyright (c) 2026 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) -import doxygen ; -import os ; -import path ; +# Adapted from Boost.Redis / Boost.Graph, following the boostorg/unordered scheme. +make build/site/index.html : build_antora.sh : @run-script ; -.doxygen = [ doxygen.name ] ; -.doxygen ?= doxygen ; - -#doxygen/gil_standalone/gil_boost.doxygen -make reference : doxyfile - : @make_doxygen - : html - $(headers) - ; - -rule make_doxygen ( targets * : sources * : properties * ) -{ - LIB_DIR on $(targets) = - [ path.native [ path.parent [ path.root - [ on $(sources[1]) return $(SEARCH) ] [ path.pwd ] ] ] ] ; -} - -if [ os.name ] = NT -{ - actions make_doxygen - { - SET LIB_DIR=$(LIB_DIR) - chdir "$(>:D)" && "$(.doxygen)" $(>:D=) - } -} -else -{ - actions make_doxygen - { - export LIB_DIR=$(LIB_DIR) - cd $(>:D) && "$(.doxygen)" $(>:D=) - } -} - -make html - : index.rst - : @sphinx-build - : . - reference - ; - -if [ os.name ] = NT -{ - actions sphinx-build { chdir "$(>:D)" && make clean && make html} -} -else +actions run-script { - actions sphinx-build { make -C "$(>:D)" clean html} + bash $(>) } ############################################################################### alias boostdoc ; explicit boostdoc ; -alias boostrelease : html ; +alias boostrelease : build/site/index.html ; explicit boostrelease ; diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 41aa56fb0e..0000000000 --- a/doc/Makefile +++ /dev/null @@ -1,133 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build -HTMLDIR = html - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest - -all: html - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(HTMLDIR) - @echo - @echo "Build finished. The HTML pages are in $(HTMLDIR)." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/BoostNumPy.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/BoostNumPy.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/BoostNumPy" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/BoostNumPy" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - make -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/README.md b/doc/README.md index de7700be63..dc5614c2b5 100644 --- a/doc/README.md +++ b/doc/README.md @@ -2,33 +2,31 @@ A simple guide about writing and building documentation for Boost.GIL. -## Prerequisites - -- Python 3 -- Install [Sphinx](https://www.sphinx-doc.org/en/master/index.html) (see `requirements.txt`) -- Install [Doxygen](http://www.doxygen.org) +The documentation is written in AsciiDoc and built with [Antora](https://antora.org/). +The C++ API reference is still generated with [Doxygen](https://www.doxygen.org) +(see `doxyfile`) and stitched into the Antora site by +`modules/ROOT/pages/reference.adoc` — this library is expected to move to a +[MrDocs](https://github.com/cppalliance/mrdocs)-generated, AsciiDoc-native +reference once an upstream MrDocs crash on GIL's header patterns is fixed; +see `mrdocs.yml` for details. +## Prerequisites -## Installation +- [Node.js](https://nodejs.org/) 20+ (for Antora) +- [Doxygen](https://www.doxygen.org) -Create Python virtual environment: +Both are available through the repository's `pixi.toml` under the `docs` +environment; from the repository root: ```console -$ python3 -m venv .venv -$ source ~/.venv/bin/activate +$ pixi run -e docs docs-build ``` -Install Sphinx and Sphinx extensions: +## Build manually ```console -(.venv)$ cd boost-root -(.venv)$ pip install -r libs/gil/doc/requirements.txt +$ cd libs/gil/doc +$ bash build_antora.sh ``` -## Build - -```console -$ echo "using doxygen ;" > ~/user-config.jam -$ cd boost-root -$ b2 libs/gil/doc -``` +The site is generated at `doc/build/site/index.html`. diff --git a/doc/_static/boost-gil.png b/doc/_static/boost-gil.png deleted file mode 100644 index 8a698f3bd0b8d4c4280f5a8bd0189e3d6998c74b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17091 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 diff --git a/doc/_static/boost-gil.svg b/doc/_static/boost-gil.svg deleted file mode 100644 index 34a84715e5..0000000000 --- a/doc/_static/boost-gil.svg +++ /dev/null @@ -1,191 +0,0 @@ - -image/svg+xmlBoost.GIL -Generic Image Library - \ No newline at end of file diff --git a/doc/_static/boost.css b/doc/_static/boost.css deleted file mode 100644 index 582f421eb7..0000000000 --- a/doc/_static/boost.css +++ /dev/null @@ -1,735 +0,0 @@ - -/*============================================================================= -Copyright (c) 2004 Joel de Guzman -http://spirit.sourceforge.net/ - -Copyright 2013 Niall Douglas additions for colors and alignment. -Copyright 2013 Paul A. Bristow additions for more colors and alignments. - -Distributed under the Boost Software License, Version 1.0. (See accompany- -ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ - -/*============================================================================= -Body defaults -=============================================================================*/ - - body - { - margin: 1em; - font-family: sans-serif; - } - -/*============================================================================= -Paragraphs -=============================================================================*/ - - p - { - text-align: left; - font-size: 10pt; - line-height: 1.15; - } - -/*============================================================================= -Program listings -=============================================================================*/ - - /* Code on paragraphs */ - p tt.computeroutput - { - font-size: 9pt; - } - - pre.synopsis - { - font-size: 9pt; - margin: 1pc 4% 0pc 4%; - padding: 0.5pc 0.5pc 0.5pc 0.5pc; - } - - .programlisting, - .screen - { - font-size: 9pt; - display: block; - margin: 1pc 4% 0pc 4%; - padding: 0.5pc 0.5pc 0.5pc 0.5pc; - } - - /* Program listings in tables don't get borders */ - td .programlisting, - td .screen - { - margin: 0pc 0pc 0pc 0pc; - padding: 0pc 0pc 0pc 0pc; - } - -/*============================================================================= -Headings -=============================================================================*/ - - h1, h2, h3, h4, h5, h6 - { - text-align: left; - margin: 1em 0em 0.5em 0em; - font-weight: bold; - } - - h1 { font-size: 140%; } - h2 { font-weight: bold; font-size: 140%; } - h3 { font-weight: bold; font-size: 130%; } - h4 { font-weight: bold; font-size: 120%; } - h5 { font-weight: normal; font-style: italic; font-size: 110%; } - h6 { font-weight: normal; font-style: italic; font-size: 100%; } - - /* Top page titles */ - title, - h1.title, - h2.title - h3.title, - h4.title, - h5.title, - h6.title, - .refentrytitle - { - font-weight: bold; - margin-bottom: 1pc; - } - - h1.title { font-size: 140% } - h2.title { font-size: 140% } - h3.title { font-size: 130% } - h4.title { font-size: 120% } - h5.title { font-size: 110% } - h6.title { font-size: 100% } - - .section h1 - { - margin: 0em 0em 0.5em 0em; - font-size: 140%; - } - - .section h2 { font-size: 140% } - .section h3 { font-size: 130% } - .section h4 { font-size: 120% } - .section h5 { font-size: 110% } - .section h6 { font-size: 100% } - - /* Code on titles */ - h1 tt.computeroutput { font-size: 140% } - h2 tt.computeroutput { font-size: 140% } - h3 tt.computeroutput { font-size: 130% } - h4 tt.computeroutput { font-size: 130% } - h5 tt.computeroutput { font-size: 130% } - h6 tt.computeroutput { font-size: 130% } - - -/*============================================================================= -Author -=============================================================================*/ - - h3.author - { - font-size: 100% - } - -/*============================================================================= -Lists -=============================================================================*/ - - li - { - font-size: 10pt; - line-height: 1.3; - } - - /* Unordered lists */ - ul - { - text-align: left; - } - - /* Ordered lists */ - ol - { - text-align: left; - } - -/*============================================================================= -Links -=============================================================================*/ - - a - { - text-decoration: none; /* no underline */ - } - - a:hover - { - text-decoration: underline; - } - -/*============================================================================= -Spirit style navigation -=============================================================================*/ - - .spirit-nav - { - text-align: right; - } - - .spirit-nav a - { - color: white; - padding-left: 0.5em; - } - - .spirit-nav img - { - border-width: 0px; - } - -/*============================================================================= -Copyright footer -=============================================================================*/ - .copyright-footer - { - text-align: right; - font-size: 70%; - } - - .copyright-footer p - { - text-align: right; - font-size: 80%; - } - -/*============================================================================= -Table of contents -=============================================================================*/ - - div.toc - { - margin: 1pc 4% 0pc 4%; - padding: 0.1pc 1pc 0.1pc 1pc; - font-size: 80%; - line-height: 1.15; - } - - .boost-toc - { - float: right; - padding: 0.5pc; - } - - /* Code on toc */ - .toc .computeroutput { font-size: 120% } - - /* No margin on nested menus */ - - .toc dl dl { margin: 0; } - -/*============================================================================= -Tables -=============================================================================*/ - - .table-title, - div.table p.title - { - margin-left: 4%; - padding-right: 0.5em; - padding-left: 0.5em; - } - - .informaltable table, - .table table - { - width: 92%; - margin-left: 4%; - margin-right: 4%; - } - - div.informaltable table, - div.table table - { - padding: 4px; - } - - /* Table Cells */ - div.informaltable table tr td, - div.table table tr td - { - padding: 0.5em; - text-align: left; - font-size: 9pt; - } - - div.informaltable table tr th, - div.table table tr th - { - padding: 0.5em 0.5em 0.5em 0.5em; - border: 1pt solid white; - font-size: 80%; - } - - table.simplelist - { - width: auto !important; - margin: 0em !important; - padding: 0em !important; - border: none !important; - } - table.simplelist td - { - margin: 0em !important; - padding: 0em !important; - text-align: left !important; - font-size: 9pt !important; - border: none !important; - } - -/*============================================================================= -Suppress margins in tables -=============================================================================*/ - - table th > *:first-child, - table td > *:first-child - { - margin-top: 0; - } - - table th > *:last-child, - table td > *:last-child - { - margin-bottom: 0; - } - -/*============================================================================= -Blurbs -=============================================================================*/ - - div.note, - div.tip, - div.important, - div.caution, - div.warning, - p.blurb - { - font-size: 9pt; /* A little bit smaller than the main text */ - line-height: 1.2; - display: block; - margin: 1pc 4% 0pc 4%; - padding: 0.5pc 0.5pc 0.5pc 0.5pc; - } - - p.blurb img - { - padding: 1pt; - } - -/*============================================================================= -Variable Lists -=============================================================================*/ - - div.variablelist - { - margin: 1em 0; - } - - /* Make the terms in definition lists bold */ - div.variablelist dl dt, - span.term - { - font-weight: bold; - font-size: 10pt; - } - - div.variablelist table tbody tr td - { - text-align: left; - vertical-align: top; - padding: 0em 2em 0em 0em; - font-size: 10pt; - margin: 0em 0em 0.5em 0em; - line-height: 1; - } - - div.variablelist dl dt - { - margin-bottom: 0.2em; - } - - div.variablelist dl dd - { - margin: 0em 0em 0.5em 2em; - font-size: 10pt; - } - - div.variablelist table tbody tr td p, - div.variablelist dl dd p - { - margin: 0em 0em 0.5em 0em; - line-height: 1; - } - -/*============================================================================= -Misc -=============================================================================*/ - - /* Title of books and articles in bibliographies */ - span.title - { - font-style: italic; - } - - span.underline - { - text-decoration: underline; - } - - span.strikethrough - { - text-decoration: line-through; - } - - /* Copyright, Legal Notice */ - div div.legalnotice p - { - text-align: left - } - -/*============================================================================= -Colors -=============================================================================*/ - - @media screen - { - body { - background-color: #FFFFFF; - color: #000000; - } - - /* Syntax Highlighting */ - .keyword { color: #0000AA; } - .identifier { color: #000000; } - .special { color: #707070; } - .preprocessor { color: #402080; } - .char { color: teal; } - .comment { color: #800000; } - .string { color: teal; } - .number { color: teal; } - .white_bkd { background-color: #FFFFFF; } - .dk_grey_bkd { background-color: #999999; } - - /* Links */ - a, a .keyword, a .identifier, a .special, a .preprocessor - a .char, a .comment, a .string, a .number - { - color: #005a9c; - } - - a:visited, a:visited .keyword, a:visited .identifier, - a:visited .special, a:visited .preprocessor a:visited .char, - a:visited .comment, a:visited .string, a:visited .number - { - color: #9c5a9c; - } - - h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, - h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover, - h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited - { - text-decoration: none; /* no underline */ - color: #000000; - } - - /* Copyright, Legal Notice */ - .copyright - { - color: #666666; - font-size: small; - } - - div div.legalnotice p - { - color: #666666; - } - - /* Program listing */ - pre.synopsis - { - border: 1px solid #DCDCDC; - } - - .programlisting, - .screen - { - border: 1px solid #DCDCDC; - } - - td .programlisting, - td .screen - { - border: 0px solid #DCDCDC; - } - - /* Blurbs */ - div.note, - div.tip, - div.important, - div.caution, - div.warning, - p.blurb - { - border: 1px solid #DCDCDC; - } - - /* Table of contents */ - div.toc - { - border: 1px solid #DCDCDC; - } - - /* Tables */ - div.informaltable table tr td, - div.table table tr td - { - border: 1px solid #DCDCDC; - } - - div.informaltable table tr th, - div.table table tr th - { - background-color: #F0F0F0; - border: 1px solid #DCDCDC; - } - - .copyright-footer - { - color: #8F8F8F; - } - - /* Misc */ - span.highlight - { - color: #00A000; - } - } - - @media print - { - /* Links */ - a - { - color: black; - } - - a:visited - { - color: black; - } - - .spirit-nav - { - display: none; - } - - /* Program listing */ - pre.synopsis - { - border: 1px solid gray; - } - - .programlisting, - .screen - { - border: 1px solid gray; - } - - td .programlisting, - td .screen - { - border: 0px solid #DCDCDC; - } - - /* Table of contents */ - div.toc - { - border: 1px solid gray; - } - - .informaltable table, - .table table - { - border: 1px solid gray; - border-collapse: collapse; - } - - /* Tables */ - div.informaltable table tr td, - div.table table tr td - { - border: 1px solid gray; - } - - div.informaltable table tr th, - div.table table tr th - { - border: 1px solid gray; - } - - table.simplelist tr td - { - border: none !important; - } - - /* Misc */ - span.highlight - { - font-weight: bold; - } - } - -/*============================================================================= -Images -=============================================================================*/ - - span.inlinemediaobject img - { - vertical-align: middle; - } - -/*============================================================================== -Super and Subscript: style so that line spacing isn't effected, see -http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&productId=1&postId=5341 -==============================================================================*/ - -sup, -sub { -height: 0; -line-height: 1; -vertical-align: baseline; -position: relative; - -} - -/* For internet explorer: */ - -* html sup, -* html sub { -vertical-align: bottom; -} - -sup { -bottom: 1ex; -} - -sub { -top: .5ex; -} - -/*============================================================================== -Indexes: pretty much the same as the TOC. -==============================================================================*/ - - .index - { - font-size: 80%; - padding-top: 0px; - padding-bottom: 0px; - margin-top: 0px; - margin-bottom: 0px; - margin-left: 0px; - } - - .index ul - { - padding-left: 3em; - } - - .index p - { - padding: 2px; - margin: 2px; - } - - .index-entry-level-0 - { - font-weight: bold; - } - - .index em - { - font-weight: bold; - } - - -/*============================================================================== -Alignment and coloring use 'role' feature, available from Quickbook 1.6 up. -Added from Niall Douglas for role color and alignment. -http://article.gmane.org/gmane.comp.lib.boost.devel/243318 -*/ - -/* Add text alignment (see http://www.w3schools.com/cssref/pr_text_text-align.asp) */ -span.aligncenter -{ - display: inline-block; width: 100%; text-align: center; -} -span.alignright -{ - display: inline-block; width: 100%; text-align: right; -} -/* alignleft is the default. */ -span.alignleft -{ - display: inline-block; width: 100%; text-align: left; -} - -/* alignjustify stretches the word spacing so that each line has equal width -within a chosen fraction of page width (here arbitrarily 20%). -*Not* useful inside table items as the column width remains the total string width. -Nor very useful, except to temporarily restrict the width. -*/ -span.alignjustify -{ - display: inline-block; width: 20%; text-align: justify; -} - -/* Text colors. -Names at http://www.w3.org/TR/2002/WD-css3-color-20020219/ 4.3. X11 color keywords. -Quickbook Usage: [role red Some red text] - -*/ -span.red { inline-block; color: red; } -span.green { color: green; } -span.lime { color: #00FF00; } -span.blue { color: blue; } -span.navy { color: navy; } -span.yellow { color: yellow; } -span.magenta { color: magenta; } -span.indigo { color: #4B0082; } -span.cyan { color: cyan; } -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/_static/boost.png b/doc/_static/boost.png deleted file mode 100644 index b4d51fcd5c9149fd77f5ca6ed2b6b1b70e8fe24f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6308 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 -image/svg+xmlBoost.GIL -Generic Image Library - \ No newline at end of file diff --git a/doc/_static/home.png b/doc/_static/home.png deleted file mode 100644 index 5584aacb097a80e66a5320312b6e4eb017af1a06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358 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 diff --git a/doc/_static/next.png b/doc/_static/next.png deleted file mode 100644 index 59800b4e87f60c0e3383ede2b384b9be0f5ffe8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7T3?v)swEqJs=3*z$5DpHG+YkL80J$mwJ|V8+ zB7)tW9mdAS+qP~A^!HU$R#>ro>7z#vFJHct5EtX@VE6z3e?1}lM4)<}k|4ieAQuK0 zgzld^2NdTl@Q5sCVBi)4Va7{$>;3=*RZCnWN`mv#O3D+9QW<;`lZ*0mQ&Tb%72Gn5 zQp@v;vWpdbBNU7b3{2X~)|>}w5Cds&E=o--$;{7F2+7P%WiT|*H!#*WFgd=yhZCqG z!qdeuq+(9;4PKV1Hyx6gnJ3)X*T8%1&d~!CcvGht8EkNzY0cztDB;<=un7h&oMQJ5 zOnAh^!sYOgmxqZbU_wKJkwW(@g)2%k8?y8!<$B~f6iH61Ubv5eVdat_!+^S!9H1Qx Mp00i_>zopr0Jz|Gn*aa+ diff --git a/doc/_static/prev.png b/doc/_static/prev.png deleted file mode 100644 index d88a40f923e3c554125f01cd366707c60cfcad04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7T3?v)swEqJs=3*z$5DpHG+YkL80J$mwJ|V8+ zB7$jYX~xFJ+qQ05v3zNuzpt9I!sW}Cx;s0Z9qba~V*daCKl2PvGf+KGNswPKkP8D0 zLif*|1B!DNctjR6FmMZjFyp1Wb$@_@swJ)wCBgY=CFO}lsSLh}$wm3OsVSL>3T~N2 zspa`a*~JRJ5eh~I1}5!gYt92Th=DXX7o{eaWaj57gkwm>gfq&(JZLf6ZeXy%aV8%R&r1O{j}20XTDX{IIykaj z4Nx#hVBm6cU}k1+>Qw1kB6gA`rFdeFa~bob(-ZFQzAc!;#K4>y+$E64`2%PJgQu&X J%Q~loCIAR&aHIeL diff --git a/doc/_static/style.css b/doc/_static/style.css deleted file mode 100644 index 4eea4df036..0000000000 --- a/doc/_static/style.css +++ /dev/null @@ -1,44 +0,0 @@ -@import url(boost.css); - -.header h1 a -{ - color: #00507f; - font-size: 200%; - font-style: italic; -} -.header h3 { margin: 1px;} -#contents -{ -/* border-bottom: solid thin black;*/ -} - -.highlight -{ - border: 1px solid #dcdcdc; - background-color: inherit; - padding: 0 1em; - margin: 0 5em; -} -#searchbox -{ - float: right; - width: auto; - margin: 0 2em; -} - -.admonition-title { font-weight: bold;} -.toctree-wrapper -{ - display: inline-block; - border: 1px solid #dcdcdc; - padding: 1em; - margin: 0 2em; -} -.toctree-wrapper .caption, -.toctree-wrapper .topic-title { font-weight: bold;} - -.footer -{ - text-align: left; - font-size: 80%; -} diff --git a/doc/_static/up.png b/doc/_static/up.png deleted file mode 100644 index 17d9c3ec491ae1ba22188ce85985623c92ffa9be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7T3?v)swEqJs=3*z$5DpHG+YkL80J&NLJ|V8+ zB7$jYY0eII+qQ05v3zNFXUFBsmjeBL)sz)x&YXGl$l-*zm`9Hu{{R1f8uJZXpf0|W zAirQB7XcVt58OEcl;JG!h%9Dc;1&X5#!GkW{s0A4OI#yLg7ec#$`gxH8GI9yi}G_* zQ!*13+%k(&%kzt}ixqq$6pRcEOxnxVoCj(U18HzBN=+=u%+FH@$;?eS^Z=hXV#3jn*#4_ x=vj5p^wF)?xpx*Du6ddKc=uuXxWffq{6A*KyDFWJxdXJF!PC{xWt~$(69DY=mmvTE diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html deleted file mode 100644 index db2436776a..0000000000 --- a/doc/_templates/layout.html +++ /dev/null @@ -1,139 +0,0 @@ -{%- macro navbar() %} -

-{%- endmacro %} - - - - - - {{ metatags }} - {%- if builder != 'htmlhelp' %} - {%- set titlesuffix = docstitle|e %} - {%- set titlesuffix = " - " + titlesuffix %} - {%- endif %} - {{ title|striptags }}{{ titlesuffix }} - {%- if builder == 'web' %} - - {%- for link, type, title in page_links %} - - {%- endfor %} - {%- else %} - - - - {%- endif %} - {%- if builder != 'htmlhelp' %} - - {%- for scriptfile in script_files %} - - {%- endfor %} - - - - {%- if use_opensearch %} - - {%- endif %} - {%- if favicon %} - - {%- endif %} - {%- endif %} -{%- block linktags %} - {%- if hasdoc('about') %} - - {%- endif %} - - - {%- if hasdoc('copyright') %} - - {%- endif %} - - {%- if parents %} - - {%- endif %} - {%- if next %} - - {%- endif %} - {%- if prev %} - - {%- endif %} -{%- endblock %} -{%- block extrahead %} {% endblock %} - - -
- - - - - - -
-

C++ Boost

-
-

-
- {%- if pagename != "search" %} - - - {%- endif %} -
-
-
-
- {%- block top_navbar %}{{ navbar() }}{% endblock %} - {% block body %} {% endblock %} - {%- block bottom_navbar %}{{ navbar() }}{% endblock %} -
- - {%- block footer %} - - {%- endblock %} - - diff --git a/doc/antora.yml b/doc/antora.yml new file mode 100644 index 0000000000..5ada83e0bf --- /dev/null +++ b/doc/antora.yml @@ -0,0 +1,22 @@ +# +# Copyright (c) 2026 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) +# +# Official repository: https://github.com/boostorg/gil +# + +name: gil +version: ~ +title: Boost.GIL +start_page: index.adoc +nav: + - modules/ROOT/nav.adoc + +# NOTE: the C++ API reference is not yet generated by MrDocs. +# MrDocs 0.8.0 segfaults on most of GIL's headers (pixel/image/locator/iterator +# families, i.e. anything built on the CRTP proxy-reference pattern) -- see the +# upstream report before re-enabling `ext: cpp-reference: config: doc/mrdocs.yml` +# here. Until then, modules/ROOT/pages/reference.adoc links to the Doxygen HTML +# reference generated alongside this Antora build (see build_antora.sh). diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index 8dff012f75..0000000000 --- a/doc/conf.py +++ /dev/null @@ -1,225 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Boost.GIL documentation build configuration file. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autosectionlabel'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Boost.GIL' -copyright = u'' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '' -# The full version, including alpha/beta/rc tags. -release = '' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# disambiguate auto-generated section anchors by prefixing -# them with the document name -autosectionlabel_prefix_document = True - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'default' - -highlight_language = 'c++' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -html_logo = '_static/gil.png' - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. The empty string is equivalent to '%b %d, %Y' -# (or a locale-dependent equivalent). -html_last_updated_fmt = '%Y-%m-%d %H:%m:%S' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -html_show_copyright = False - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'BoostGILdoc' - -# This value determines the text for the permalink; it defaults to "¶". -# Set it to None or the empty string to disable permalinks. -html_add_permalinks = '' - -# -- Options for LaTeX output -------------------------------------------------- - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'BoostGIL.tex', u'Boost.GIL Documentation', - u'Stefan Seefeld', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'boostgil', u'Boost.GIL Documentation', - [u'Stefan Seefeld'], 1) -] diff --git a/doc/design/channel.rst b/doc/design/channel.rst deleted file mode 100644 index 75608c3f78..0000000000 --- a/doc/design/channel.rst +++ /dev/null @@ -1,204 +0,0 @@ -Channel -======= - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -A channel indicates the intensity of a color component (for example, the red -channel in an RGB pixel). Typical channel operations are getting, comparing -and setting the channel values. Channels have associated minimum and maximum -value. GIL channels model the following concept: - -.. code-block:: cpp - - concept ChannelConcept : EqualityComparable - { - typename value_type = T; // use channel_traits::value_type to access it - where ChannelValueConcept; - typename reference = T&; // use channel_traits::reference to access it - typename pointer = T*; // use channel_traits::pointer to access it - typename const_reference = const T&; // use channel_traits::const_reference to access it - typename const_pointer = const T*; // use channel_traits::const_pointer to access it - static const bool is_mutable; // use channel_traits::is_mutable to access it - - static T min_value(); // use channel_traits::min_value to access it - static T max_value(); // use channel_traits::max_value to access it - }; - - concept MutableChannelConcept : Swappable, Assignable {}; - - concept ChannelValueConcept : Regular {}; - -GIL allows built-in integral and floating point types to be channels. -Therefore the associated types and range information are defined in -``channel_traits`` with the following default implementation: - -.. code-block:: cpp - - template - struct channel_traits - { - typedef T value_type; - typedef T& reference; - typedef T* pointer; - typedef T& const const_reference; - typedef T* const const_pointer; - - static value_type min_value() { return std::numeric_limits::min(); } - static value_type max_value() { return std::numeric_limits::max(); } - }; - -Two channel types are *compatible* if they have the same value type: - -.. code-block:: cpp - - concept ChannelsCompatibleConcept - { - where SameType; - }; - -A channel may be *convertible* to another channel: - -.. code-block:: cpp - - template - concept ChannelConvertibleConcept - { - Dst channel_convert(Src); - }; - -Note that ``ChannelConcept`` and ``MutableChannelConcept`` do not require a -default constructor. Channels that also support default construction (and thus -are regular types) model ``ChannelValueConcept``. -To understand the motivation for this distinction, consider a 16-bit RGB pixel -in a "565" bit pattern. Its channels correspond to bit ranges. To support such -channels, we need to create a custom proxy class corresponding to a reference -to a sub-byte channel. -Such a proxy reference class models only ``ChannelConcept``, because, similar -to native C++ references, it may not have a default constructor. - -Note also that algorithms may impose additional requirements on channels, -such as support for arithmetic operations. - -.. seealso:: - - - `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 ------- - -All C++11 fundamental integer and float point types are valid channels. - -The minimum and maximum values of a channel modeled by a built-in type -correspond to the minimum and maximum physical range of the built-in type, as -specified by its ``std::numeric_limits``. Sometimes the physical range is not -appropriate. GIL provides ``scoped_channel_value``, a model for a channel -adapter that allows for specifying a custom range. -We use it to define a ``[0..1]`` floating point channel type as follows: - -.. code-block:: cpp - - struct float_zero { static float apply() { return 0.0f; } }; - struct float_one { static float apply() { return 1.0f; } }; - typedef scoped_channel_value bits32f; - -GIL also provides models for channels corresponding to ranges of bits: - -.. code-block:: cpp - - // Value of a channel defined over NumBits bits. Models ChannelValueConcept - template class packed_channel_value; - - // Reference to a channel defined over NumBits bits. Models ChannelConcept - template // true if the reference is mutable - class packed_channel_reference; - - // Reference to a channel defined over NumBits bits. Its FirstBit is a run-time parameter. Models ChannelConcept - template // true if the reference is mutable - class packed_dynamic_channel_reference; - -Note that there are two models of a reference proxy which differ based on -whether the offset of the channel range is specified as a template or a -run-time parameter. The first model is faster and more compact while the -second model is more flexible. For example, the second model allows us to -construct an iterator over bit range channels. - -Algorithms ----------- - -Here is how to construct the three channels of a 16-bit "565" pixel and set -them to their maximum value: - -.. code-block:: cpp - - using channel16_0_5_reference_t = packed_channel_reference<0, 5, true>; - using channel16_5_6_reference_t = packed_channel_reference<5, 6, true>; - using channel16_11_5_reference_t = packed_channel_reference<11, 5, true>; - - std::uint16_t data=0; - channel16_0_5_reference_t channel1(&data); - channel16_5_6_reference_t channel2(&data); - channel16_11_5_reference_t channel3(&data); - - channel1 = channel_traits::max_value(); - channel2 = channel_traits::max_value(); - channel3 = channel_traits::max_value(); - assert(data == 65535); - -Assignment, equality comparison and copy construction are defined only between -compatible channels: - -.. code-block:: cpp - - packed_channel_value<5> channel_6bit = channel1; - channel_6bit = channel3; - - // compile error: Assignment between incompatible channels - //channel_6bit = channel2; - -All channel models provided by GIL are pairwise convertible: - -.. code-block:: cpp - - channel1 = channel_traits::max_value(); - assert(channel1 == 31); - - bits16 chan16 = channel_convert(channel1); - assert(chan16 == 65535); - -Channel conversion is a lossy operation. GIL's channel conversion is a linear -transformation between the ranges of the source and destination channel. -It maps precisely the minimum to the minimum and the maximum to the maximum. -(For example, to convert from uint8_t to uint16_t GIL does not do a bit shift -because it will not properly match the maximum values. Instead GIL multiplies -the source by 257.) - -All channel models that GIL provides are convertible from/to an integral or -floating point type. Thus they support arithmetic operations. Here are the -channel-level algorithms that GIL provides: - -.. code-block:: cpp - - // Converts a source channel value into a destination channel. - // Linearly maps the value of the source into the range of the destination. - template - typename channel_traits::value_type channel_convert(SrcChannel src); - - // returns max_value - x + min_value - template - typename channel_traits::value_type channel_invert(Channel x); - - // returns a * b / max_value - template - typename channel_traits::value_type channel_multiply(Channel a, Channel b); diff --git a/doc/design/color_base.rst b/doc/design/color_base.rst deleted file mode 100644 index f774d2d62c..0000000000 --- a/doc/design/color_base.rst +++ /dev/null @@ -1,245 +0,0 @@ -Color Base -========== - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -A color base is a container of color elements. The most common use of color -base is in the implementation of a pixel, in which case the color elements are -channel values. The color base concept, however, can be used in other -scenarios. For example, a planar pixel has channels that are not contiguous in -memory. Its reference is a proxy class that uses a color base whose elements -are channel references. Its iterator uses a color base whose elements are -channel iterators. - -Color base models must satisfy the following concepts: - -.. code-block:: cpp - - concept ColorBaseConcept - : CopyConstructible, EqualityComparable - { - // a GIL layout (the color space and element permutation) - typename layout_t; - - // The type of K-th element - template struct kth_element_type; - where Metafunction; - - // The result of at_c - template struct kth_element_const_reference_type; - where Metafunction; - - template kth_element_const_reference_type::type at_c(T); - - template where { ColorBasesCompatibleConcept } - T::T(T2); - template where { ColorBasesCompatibleConcept } - bool operator==(const T&, const T2&); - template where { ColorBasesCompatibleConcept } - bool operator!=(const T&, const T2&); - - }; - - concept MutableColorBaseConcept - : Assignable, Swappable - { - template struct kth_element_reference_type; - where Metafunction; - - template kth_element_reference_type::type at_c(T); - - template where { ColorBasesCompatibleConcept } - T& operator=(T&, const T2&); - }; - - concept ColorBaseValueConcept : MutableColorBaseConcept, Regular - { - }; - - concept HomogeneousColorBaseConcept - { - // For all K in [0 ... size::value-1): - // where SameType::type, kth_element_type::type>; - kth_element_const_reference_type<0>::type dynamic_at_c(const CB&, std::size_t n) const; - }; - - concept MutableHomogeneousColorBaseConcept - : HomogeneousColorBaseConcept - { - kth_element_reference_type<0>::type dynamic_at_c(const CB&, std::size_t n); - }; - - concept HomogeneousColorBaseValueConcept - : MutableHomogeneousColorBaseConcept, Regular - { - }; - - concept ColorBasesCompatibleConcept - { - where SameType; - // also, for all K in [0 ... size::value): - // where Convertible::type, kth_semantic_element_type::type>; - // where Convertible::type, kth_semantic_element_type::type>; - }; - -A color base must have an associated layout (which consists of a color space, -as well as an ordering of the channels). There are two ways to index the -elements of a color base: A physical index corresponds to the way they are -ordered in memory, and a semantic index corresponds to the way the elements -are ordered in their color space. For example, in the RGB color space the -elements are ordered as ``{red_t, green_t, blue_t}``. For a color base with -a BGR layout, the first element in physical ordering is the blue element, -whereas the first semantic element is the red one. Models of -``ColorBaseConcept`` are required to provide the ``at_c(ColorBase)`` -function, which allows for accessing the elements based on their physical -order. GIL provides a ``semantic_at_c(ColorBase)`` function (described -later) which can operate on any model of ColorBaseConcept and returns the -corresponding semantic element. - -Two color bases are *compatible* if they have the same color space and their -elements (paired semantically) are convertible to each other. - -Models ------- - -GIL provides a model for a homogeneous color base (a color base whose elements -all have the same type). - -.. code-block:: cpp - - namespace detail - { - template - struct homogeneous_color_base; - } - -It is used in the implementation of GIL's pixel, planar pixel reference and -planar pixel iterator. Another model of ``ColorBaseConcept`` is -``packed_pixel`` - it is a pixel whose channels are bit ranges. - -See the :doc:`pixel` section for more. - -Algorithms ----------- - -GIL provides the following functions and metafunctions operating on color -bases: - -.. code-block:: cpp - - // 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) - template struct kth_semantic_element_reference_type; - template struct kth_semantic_element_const_reference_type; - - // Returns a reference to the element with K-th semantic index. - template - typename kth_semantic_element_reference_type::type semantic_at_c(ColorBase& p) - template - typename kth_semantic_element_const_reference_type::type semantic_at_c(const ColorBase& p) - - // Returns the type of the return value of get_color(color_base) - template struct color_reference_t; - template struct color_const_reference_t; - - // Returns a reference to the element corresponding to the given color - template - typename color_reference_t::type get_color(ColorBase& cb, Color=Color()); - template - typename color_const_reference_t::type get_color(const ColorBase& cb, Color=Color()); - - // Returns the element type of the color base. Defined for homogeneous color bases only - template struct element_type; - template struct element_reference_type; - template struct element_const_reference_type; - -GIL also provides the following algorithms which operate on color bases. -Note that they all pair the elements semantically: - -.. code-block:: cpp - - // Equivalents to std::equal, std::copy, std::fill, std::generate - template bool static_equal(const CB1& p1, const CB2& p2); - template void static_copy(const Src& src, Dst& dst); - template void static_generate(CB& dst,Op op); - - // Equivalents to std::transform - template Op static_transform( CB&,Dst&,Op); - template Op static_transform(const CB&,Dst&,Op); - template Op static_transform( CB1&, CB2&,Dst&,Op); - template Op static_transform(const CB1&, CB2&,Dst&,Op); - template Op static_transform( CB1&,const CB2&,Dst&,Op); - template Op static_transform(const CB1&,const CB2&,Dst&,Op); - - // Equivalents to std::for_each - template Op static_for_each( CB1&,Op); - template Op static_for_each(const CB1&,Op); - template Op static_for_each( CB1&, CB2&,Op); - template Op static_for_each( CB1&,const CB2&,Op); - template Op static_for_each(const CB1&, CB2&,Op); - template Op static_for_each(const CB1&,const CB2&,Op); - template Op static_for_each( CB1&, CB2&, CB3&,Op); - template Op static_for_each( CB1&, CB2&,const CB3&,Op); - template Op static_for_each( CB1&,const CB2&, CB3&,Op); - template Op static_for_each( CB1&,const CB2&,const CB3&,Op); - template Op static_for_each(const CB1&, CB2&, CB3&,Op); - template Op static_for_each(const CB1&, CB2&,const CB3&,Op); - template Op static_for_each(const CB1&,const CB2&, CB3&,Op); - template Op static_for_each(const CB1&,const CB2&,const CB3&,Op); - - // The following algorithms are only defined for homogeneous color bases: - // Equivalent to std::fill - template void static_fill(HCB& p, const Element& v); - - // Equivalents to std::min_element and std::max_element - template typename element_const_reference_type::type static_min(const HCB&); - template typename element_reference_type::type static_min( HCB&); - template typename element_const_reference_type::type static_max(const HCB&); - template typename element_reference_type::type static_max( HCB&); - -These algorithms are designed after the corresponding STL algorithms, except -that instead of ranges they take color bases and operate on their elements. -In addition, they are implemented with a compile-time recursion (thus the -prefix "static\_"). Finally, they pair the elements semantically instead of -based on their physical order in memory. - -For example, here is the implementation of ``static_equal``: - -.. code-block:: cpp - - namespace detail - { - template struct element_recursion - { - template - static bool static_equal(const P1& p1, const P2& p2) - { - return element_recursion::static_equal(p1,p2) && - semantic_at_c(p1)==semantic_at_c(p2); - } - }; - template <> struct element_recursion<0> - { - template - static bool static_equal(const P1&, const P2&) { return true; } - }; - } - - template - bool static_equal(const P1& p1, const P2& p2) - { - gil_function_requires >(); - return detail::element_recursion::value>::static_equal(p1,p2); - } - -This algorithm is used when invoking ``operator==`` on two pixels, for -example. By using semantic accessors we are properly comparing an RGB pixel to -a BGR pixel. Notice also that all of the above algorithms taking more than one -color base require that they all have the same color space. diff --git a/doc/design/color_space.rst b/doc/design/color_space.rst deleted file mode 100644 index 7c74083eb1..0000000000 --- a/doc/design/color_space.rst +++ /dev/null @@ -1,89 +0,0 @@ -Color Space and Layout -====================== - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -A color space captures the set and interpretation of channels comprising a -pixel. In Boost.GIL, color space is defined as an MPL random access sequence -containing the types of all elements in the color space. - -Two color spaces are considered *compatible* if they are equal (i.e. have the -same set of colors in the same order). - -.. seealso:: - - - `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 ------- - -GIL currently provides the following color spaces: - -- ``gray_t`` -- ``rgb_t`` -- ``rgba_t`` -- ``cmyk_t`` - -It also provides unnamed N-channel color spaces of two to five channels: - -- ``devicen_t<2>`` -- ``devicen_t<3>`` -- ``devicen_t<4>`` -- ``devicen_t<5>`` - -Besides the standard layouts, it also provides: - -- ``bgr_layout_t`` -- ``bgra_layout_t`` -- ``abgr_layout_t`` -- ``argb_layout_t`` - -As an example, here is how GIL defines the RGBA color space: - -.. code-block:: cpp - - struct red_t {}; - struct green_t {}; - struct blue_t {}; - struct alpha_t {}; - 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 -``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. - -The mapping of channels is specified by ``ChannelMappingConcept``, which is -an MPL random access sequence of integral types. -A color space and its associated mapping are often used together. - -Thus they are grouped in GIL's layout: - -.. code-block:: cpp - - template - < - typename ColorSpace, - typename ChannelMapping = mp11::make_integer_sequence::value> - > - struct layout - { - using color_space_t = ColorSpace; - using channel_mapping_t = ChannelMapping; - }; - -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>; diff --git a/doc/design/concepts.rst b/doc/design/concepts.rst deleted file mode 100644 index 7aac361380..0000000000 --- a/doc/design/concepts.rst +++ /dev/null @@ -1,70 +0,0 @@ -Concepts -======== - -All constructs in GIL are models of GIL concepts. A *concept* is a set of -requirements that a type (or a set of related types) must fulfill to be used -correctly in generic algorithms. The requirements include syntactic and -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 at -`Generic Programming in ConceptC++ `_ - -In this document we will use a syntax for defining concepts that is described -in the C++ standard proposal paper -`[N2081] Concepts `_. - -Here are some common concepts that will be used in GIL. -Most of them are defined at the -`ConceptC++ Concept Web `_: - -.. code-block:: cpp - - auto concept DefaultConstructible - { - T::T(); - }; - - auto concept CopyConstructible - { - T::T(T); - T::~T(); - }; - - auto concept Assignable - { - typename result_type; - result_type operator=(T&, U); - }; - - auto concept EqualityComparable - { - bool operator==(T x, U y); - bool operator!=(T x, U y) { return !(x==y); } - }; - - concept SameType { /* unspecified */ }; - template concept_map SameType { /* unspecified */ }; - - auto concept Swappable - { - void swap(T& t, T& u); - }; - -Here are some additional basic concepts that GIL needs: - -.. code-block:: cpp - - auto concept Regular : - DefaultConstructible, - CopyConstructible, - EqualityComparable, - Assignable, - Swappable - {}; - - auto concept Metafunction - { - typename type; - }; diff --git a/doc/design/dynamic_image.rst b/doc/design/dynamic_image.rst deleted file mode 100644 index ec9b01e3fb..0000000000 --- a/doc/design/dynamic_image.rst +++ /dev/null @@ -1,211 +0,0 @@ -Dynamic images and image views -============================== - -The GIL extension called ``dynamic_image`` allows for images, image views -or any GIL constructs to have their parameters defined at run time. - -The color space, channel depth, channel ordering, and interleaved/planar -structure of an image are defined by the type of its template argument, which -makes them compile-time bound. Often some of these parameters are available -only at run time. Consider, for example, writing a module that opens the image -at a given file path, rotates it and saves it back in its original color space -and channel depth. How can we possibly write this using our generic image? -What type is the image loading code supposed to return? - -Here is an example: - -.. code-block:: cpp - - #include - using namespace boost; - - #define ASSERT_SAME(A,B) static_assert(is_same< A,B >::value, "") - - // Create any_image class (or any_image_view) class with a set of allowed images - typedef any_image my_any_image_t; - - // Associated view types are available (equivalent to the ones in image_t) - typedef any_image_view AV; - ASSERT_SAME(my_any_image_t::view_t, AV); - - typedef any_image_view> CAV; - ASSERT_SAME(my_any_image_t::const_view_t, CAV); - ASSERT_SAME(my_any_image_t::const_view_t, my_any_image_t::view_t::const_t); - - typedef any_image_view SAV; - ASSERT_SAME(typename dynamic_x_step_type::type, SAV); - - // Assign it a concrete image at run time: - my_any_image_t myImg = my_any_image_t(rgb8_image_t(100,100)); - - // Change it to another at run time. The previous image gets destroyed - myImg = cmyk16_planar_image_t(200,100); - - // Assigning to an image not in the allowed set throws an exception - myImg = gray8_image_t(); // will throw std::bad_cast - -The ``any_image`` and ``any_image_view`` subclass from Boost.Variant2 ``variant`` class, -a never valueless variant type, compatible with ``std::variant`` in C++17. - -GIL ``any_image_view`` and ``any_image`` are subclasses of ``variant``: - -.. code-block:: cpp - - template - class any_image_view : public variant - { - public: - typedef ... const_t; // immutable equivalent of this - typedef std::ptrdiff_t x_coord_t; - typedef std::ptrdiff_t y_coord_t; - typedef point point_t; - using size_type = std::size_t; - - any_image_view(); - template explicit any_image_view(const T& obj); - any_image_view(const any_image_view& v); - - template any_image_view& operator=(const T& obj); - any_image_view& operator=(const any_image_view& v); - - // parameters of the currently instantiated view - std::size_t num_channels() const; - point_t dimensions() const; - size_type size() const; - x_coord_t width() const; - y_coord_t height() const; - }; - - template - class any_image : public variant - { - public: - typedef ... const_view_t; - typedef ... view_t; - typedef std::ptrdiff_t x_coord_t; - typedef std::ptrdiff_t y_coord_t; - typedef point point_t; - - any_image(); - template explicit any_image(const T& obj); - template explicit any_image(T& obj, bool do_swap); - any_image(const any_image& v); - - template any_image& operator=(const T& obj); - any_image& operator=(const any_image& v); - - void recreate(const point_t& dims, unsigned alignment=1); - void recreate(x_coord_t width, y_coord_t height, unsigned alignment=1); - - std::size_t num_channels() const; - point_t dimensions() const; - x_coord_t width() const; - y_coord_t height() const; - }; - -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 -typically have time complexity at least linear on the number of -pixels, the single switch statement of image view variant adds -practically no measurable performance overhead compared to templated -image views. - -Variants behave like the underlying type. Their copy constructor will -invoke the copy constructor of the underlying instance. Equality -operator will check if the two instances are of the same type and then -invoke their ``operator==``, etc. The default constructor of a variant -will default-construct the first type. That means that -``any_image_view`` has shallow default-constructor, copy-constructor, -assignment and equality comparison, whereas ``any_image`` has deep -ones. - -It is important to note that even though ``any_image_view`` and -``any_image`` resemble the static ``image_view`` and ``image``, they -do not model the full requirements of ``ImageViewConcept`` and -``ImageConcept``. In particular they don't provide access to the -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 ``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 -``copy_pixels``: - -.. code-block:: cpp - - rgb8_view_t v1(...); // concrete image view - bgr8_view_t v2(...); // concrete image view compatible with v1 and of the same size - any_image_view av(...); // run-time specified image view - - // Copies the pixels from v1 into v2. - // If the pixels are incompatible triggers compile error - copy_pixels(v1,v2); - - // The source or destination (or both) may be run-time instantiated. - // If they happen to be incompatible, throws std::bad_cast - copy_pixels(v1, av); - copy_pixels(av, v2); - copy_pixels(av, av); - -By having algorithm overloads supporting dynamic constructs, we create -a base upon which it is possible to write algorithms that can work -with either compile-time or runtime images or views. The following -code, for example, uses the GIL I/O extension to turn an image on disk -upside down: - -.. code-block:: cpp - - #include - - template // Could be rgb8_image_t or any_image<...> - void save_180rot(const std::string& file_name) - { - Image 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 -because all functions it uses have overloads taking runtime -constructs. For example, here is how ``rotated180_view`` is -implemented: - -.. code-block:: cpp - - // implementation using templated view - template - typename dynamic_xy_step_type::type rotated180_view(const View& src) { ... } - - 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)); - } - }; - } - - // overloading of the function using variant. Takes and returns run-time bound view. - // The returned view has a dynamic step - template inline - typename dynamic_xy_step_type>::type rotated180_view(const any_image_view& src) - { - 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 -more than one variant) because they instantiate the algorithm for -every possible model that the variant can take. This can take a toll -on compile time and executable size. Despite these limitations, -``variant`` is a powerful technique that allows us to combine the -speed of compile-time resolution with the flexibility of run-time -resolution. It allows us to treat images of different parameters -uniformly as a collection and store them in the same container. diff --git a/doc/design/examples.rst b/doc/design/examples.rst deleted file mode 100644 index a8d2ad4415..0000000000 --- a/doc/design/examples.rst +++ /dev/null @@ -1,209 +0,0 @@ -Examples -======== - -.. contents:: - :local: - :depth: 2 - -Pixel-level Operations ----------------------- - -Here are some operations you can do with pixel values, pixel pointers and -pixel references: - -.. code-block:: cpp - - rgb8_pixel_t p1(255,0,0); // make a red RGB pixel - bgr8_pixel_t p2 = p1; // RGB and BGR are compatible and the channels will be properly mapped. - assert(p1==p2); // p2 will also be red. - assert(p2[0]!=p1[0]); // operator[] gives physical channel order (as laid down in memory) - assert(semantic_at_c<0>(p1)==semantic_at_c<0>(p2)); // this is how to compare the two red channels - get_color(p1,green_t()) = get_color(p2,blue_t()); // channels can also be accessed by name - - const unsigned char* r; - const unsigned char* g; - const unsigned char* b; - rgb8c_planar_ptr_t ptr(r,g,b); // constructing const planar pointer from const pointers to each plane - - rgb8c_planar_ref_t ref=*ptr; // just like built-in reference, dereferencing a planar pointer returns a planar reference - - p2=ref; p2=p1; p2=ptr[7]; p2=rgb8_pixel_t(1,2,3); // planar/interleaved references and values to RGB/BGR can be freely mixed - - //rgb8_planar_ref_t ref2; // compile error: References have no default constructors - //ref2=*ptr; // compile error: Cannot construct non-const reference by dereferencing const pointer - //ptr[3]=p1; // compile error: Cannot set the fourth pixel through a const pointer - //p1 = pixel();// compile error: Incompatible channel depth - //p1 = pixel();// compile error: Incompatible color space (even though it has the same number of channels) - //p1 = pixel();// compile error: Incompatible color space (even though it contains red, green and blue channels) - -Here is how to use pixels in generic code: - -.. code-block:: cpp - - template - void gray_to_rgb(const GrayPixel& src, RGBPixel& dst) - { - gil_function_requires >(); - gil_function_requires >(); - - typedef typename color_space_type::type gray_cs_t; - static_assert(boost::is_same::value, ""); - - typedef typename color_space_type::type rgb_cs_t; - static_assert(boost::is_same::value, ""); - - typedef typename channel_type::type gray_channel_t; - typedef typename channel_type::type rgb_channel_t; - - gray_channel_t gray = get_color(src,gray_color_t()); - static_fill(dst, channel_convert(gray)); - } - - // example use patterns: - - // converting gray l-value to RGB and storing at (5,5) in a 16-bit BGR interleaved image: - bgr16_view_t b16(...); - gray_to_rgb(gray8_pixel_t(33), b16(5,5)); - - // storing the first pixel of an 8-bit grayscale image as the 5-th pixel of 32-bit planar RGB image: - rgb32f_planar_view_t rpv32; - gray8_view_t gv8(...); - gray_to_rgb(*gv8.begin(), rpv32[5]); - -As the example shows, both the source and the destination can be references or -values, planar or interleaved, as long as they model ``PixelConcept`` and -``MutablePixelConcept`` respectively. - - -Resizing image canvas ---------------------- - -Resizing an image canvas means adding a buffer of pixels around existing -pixels. Size of canvas of an image can never be smaller than the image itself. - -Suppose we want to convolve an image with multiple kernels, the largest of -which is 2K+1 x 2K+1 pixels. It may be worth creating a margin of K pixels -around the image borders. Here is how to do it: - -.. code-block:: cpp - - template // Models ImageConcept (the returned image) - void create_with_margin(const SrcView& src, int k, DstImage& result) - { - gil_function_requires >(); - gil_function_requires >(); - gil_function_requires >(); - - result=DstImage(src.width()+2*k, src.height()+2*k); - typename DstImage::view_t centerImg=subimage_view(view(result), k,k,src.width(),src.height()); - std::copy(src.begin(), src.end(), centerImg.begin()); - } - -We allocated a larger image, then we used ``subimage_view`` to create a -shallow image of its center area of top left corner at (k,k) and of identical -size as ``src``, and finally we copied ``src`` into that center image. If the -margin needs initialization, we could have done it with ``fill_pixels``. Here -is how to simplify this code using the ``copy_pixels`` algorithm: - -.. code-block:: cpp - - template - void create_with_margin(const SrcView& src, int k, DstImage& result) - { - result.recreate(src.width()+2*k, src.height()+2*k); - copy_pixels(src, subimage_view(view(result), k,k,src.width(),src.height())); - } - -(Note also that ``image::recreate`` is more efficient than ``operator=``, as -the latter will do an unnecessary copy construction.) Not only does the above -example work for planar and interleaved images of any color space and pixel -depth; it is also optimized. GIL overrides ``std::copy`` - when called on two -identical interleaved images with no padding at the end of rows, it simply -does a ``memmove``. For planar images it does ``memmove`` for each channel. -If one of the images has padding, (as in our case) it will try to do -``memmove`` for each row. When an image has no padding, it will use its -lightweight horizontal iterator (as opposed to the more complex 1D image -iterator that has to check for the end of rows). It chooses the fastest method, -taking into account both static and run-time parameters. - -Histogram ---------- - -The histogram can be computed by counting the number of pixel values that fall -in each bin. The following method takes a grayscale (one-dimensional) image -view, since only grayscale pixels are convertible to integers: - -.. code-block:: cpp - - template - void grayimage_histogram(const GrayView& img, R& hist) - { - for (typename GrayView::iterator it=img.begin(); it!=img.end(); ++it) - ++hist[*it]; - } - -Using ``boost::lambda`` and GIL's ``for_each_pixel`` algorithm, we can write -this more compactly: - -.. code-block:: cpp - - template - void grayimage_histogram(const GrayView& v, R& hist) - { - for_each_pixel(v, ++var(hist)[_1]); - } - -Where ``for_each_pixel`` invokes ``std::for_each`` and ``var`` and ``_1`` are -``boost::lambda`` constructs. To compute the luminosity histogram, we call the -above method using the grayscale view of an image: - -.. code-block:: cpp - - template - void luminosity_histogram(const View& v, R& hist) - { - grayimage_histogram(color_converted_view(v),hist); - } - -This is how to invoke it: - -.. code-block:: cpp - - unsigned char hist[256]; - std::fill(hist,hist+256,0); - luminosity_histogram(my_view,hist); - -If we want to view the histogram of the second channel of the image in the top -left 100x100 area, we call: - -.. code-block:: cpp - - grayimage_histogram(nth_channel_view(subimage_view(img,0,0,100,100),1),hist); - -No pixels are copied and no extra memory is allocated - the code operates -directly on the source pixels, which could be in any supported color space and -channel depth. They could be either planar or interleaved. - -Using image views ------------------ - -The following code illustrates the power of using image views: - -.. code-block:: cpp - - jpeg_read_image("monkey.jpg", img); - step1=view(img); - step2=subimage_view(step1, 200,300, 150,150); - step3=color_converted_view(step2); - step4=rotated180_view(step3); - step5=subsampled_view(step4, 2,1); - jpeg_write_view("monkey_transform.jpg", step5); - -The intermediate images are shown here: - -.. image:: ../images/monkey_steps.jpg - -Notice that no pixels are ever copied. All the work is done inside -``jpeg_write_view``. If we call our ``luminosity_histogram`` with -``step5`` it will do the right thing. diff --git a/doc/design/extending.rst b/doc/design/extending.rst deleted file mode 100644 index 8870eb8d38..0000000000 --- a/doc/design/extending.rst +++ /dev/null @@ -1,166 +0,0 @@ -Extending -========= - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -You can define your own pixel iterators, locators, image views, -images, channel types, color spaces and algorithms. You can make -virtual images that live on the disk, inside a jpeg file, somewhere on -the internet, or even fully-synthetic images such as the Mandelbrot -set. As long as they properly model the corresponding concepts, they -will work with any existing GIL code. Most such extensions require no -changes to the library and can thus be supplied in another module. - -Defining new color spaces -------------------------- - -Each color space is in a separate file. To add a new color space, just -copy one of the existing ones (like rgb.hpp) and change it -accordingly. If you want color conversion support, you will have to -provide methods to convert between it and the existing color spaces -(see color_convert.h). For convenience you may want to provide useful -typedefs for pixels, pointers, references and images with the new -color space (see typedefs.h). - -Defining new channel types --------------------------- - -Most of the time you don't need to do anything special to use a new -channel type. You can just use it: - -.. code-block:: cpp - - typedef pixel rgb64_pixel_t; // 64 bit RGB pixel - typedef rgb64_pixel* rgb64_pixel_ptr_t;// pointer to 64-bit interleaved data - typedef image_type::type rgb64_image_t; // 64-bit interleaved image - -If you want to use your own channel class, you will need to provide a -specialization of ``channel_traits`` for it (see channel.hpp). If you -want to do conversion between your and existing channel types, you -will need to provide an overload of ``channel_convert``. - -Overloading color conversion ----------------------------- - -Suppose you want to provide your own color conversion. For example, -you may want to implement higher quality color conversion using color -profiles. Typically you may want to redefine color conversion only in -some instances and default to GIL's color conversion in all other -cases. Here is, for example, how to overload color conversion so that -color conversion to gray inverts the result but everything else -remains the same: - -.. code-block:: cpp - - // make the default use GIL's default - template - struct my_color_converter_impl - : public default_color_converter_impl {}; - - // provide specializations only for cases you care about - // (in this case, if the destination is grayscale, invert it) - template - struct my_color_converter_impl - { - template // Model PixelConcept - void operator()(const SrcP& src, DstP& dst) const - { - default_color_converter_impl()(src,dst); - get_color(dst,gray_color_t())=channel_invert(get_color(dst,gray_color_t())); - } - }; - - // create a color converter object that dispatches to your own implementation - struct my_color_converter - { - template // Model PixelConcept - void operator()(const SrcP& src,DstP& dst) const - { - typedef typename color_space_type::type SrcColorSpace; - typedef typename color_space_type::type DstColorSpace; - my_color_converter_impl()(src,dst); - } - }; - -GIL color conversion functions take the color converter as an -optional parameter. You can pass your own color converter: - -.. code-block:: cpp - - color_converted_view(img_view,my_color_converter()); - -Defining new image views ------------------------- - -You can provide your own pixel iterators, locators and views, -overriding either the mechanism for getting from one pixel to the next -or doing an arbitrary pixel transformation on dereference. For -example, let's look at the implementation of ``color_converted_view`` -(an image factory method that, given any image view, returns a new, -otherwise identical view, except that color conversion is performed on -pixel access). First we need to define a model of -``PixelDereferenceAdaptorConcept``; a function object that will be -called when we dereference a pixel iterator. It will call -``color_convert`` to convert to the destination pixel type: - -.. code-block:: cpp - - template // Destination pixel value (models PixelValueConcept) - class color_convert_deref_fn - { - public: - typedef color_convert_deref_fn const_t; - typedef DstP value_type; - typedef value_type reference; // read-only dereferencing - typedef const value_type& const_reference; - typedef SrcConstRefP argument_type; - typedef reference result_type; - static bool constexpr is_mutable = false; - - result_type operator()(argument_type srcP) const { - result_type dstP; - color_convert(srcP,dstP); - return dstP; - } - }; - -We then use the ``add_deref`` member struct of image views to construct the -type of a view that invokes a given function object (``deref_t``) upon -dereferencing. In our case, it performs color conversion: - -.. code-block:: cpp - - template - struct color_converted_view_type - { - private: - typedef typename SrcView::const_t::reference src_pix_ref; // const reference to pixel in SrcView - typedef color_convert_deref_fn deref_t; // the dereference adaptor that performs color conversion - typedef typename SrcView::template add_deref add_ref_t; - public: - typedef typename add_ref_t::type type; // the color converted view type - static type make(const SrcView& sv) { return add_ref_t::make(sv, deref_t()); } - }; - -Finally our ``color_converted_view`` code simply creates color-converted view -from the source view: - -.. code-block:: cpp - - template inline - typename color_converted_view_type::type color_convert_view(const View& src) - { - return color_converted_view_type::make(src); - } - -(The actual color convert view transformation is slightly more -complicated, as it takes an optional color conversion object, which -allows users to specify their own color conversion methods.) See the -GIL tutorial for an example of creating a virtual image view that -defines the Mandelbrot set. diff --git a/doc/design/image.rst b/doc/design/image.rst deleted file mode 100644 index d1451ee75e..0000000000 --- a/doc/design/image.rst +++ /dev/null @@ -1,103 +0,0 @@ -Image -===== - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -An image is a container that owns the pixels of a given image view. -It allocates them in its constructor and deletes them in the destructor. -It has a deep assignment operator and copy constructor. Images are used -rarely, just when data ownership is important. Most STL algorithms operate on -ranges, not containers. Similarly most GIL algorithms operate on image views -(which images provide). - -In the most general form images are N-dimensional and satisfy the following -concept: - -.. code-block:: cpp - - concept RandomAccessNDImageConcept : Regular - { - typename view_t; where MutableRandomAccessNDImageViewConcept; - typename const_view_t = view_t::const_t; - typename point_t = view_t::point_t; - typename value_type = view_t::value_type; - typename allocator_type; - - Img::Img(point_t dims, std::size_t alignment=0); - Img::Img(point_t dims, value_type fill_value, std::size_t alignment); - - void Img::recreate(point_t new_dims, std::size_t alignment=0); - void Img::recreate(point_t new_dims, value_type fill_value, std::size_t alignment); - - const point_t& Img::dimensions() const; - const const_view_t& const_view(const Img&); - const view_t& view(Img&); - }; - -Two-dimensional images have additional requirements: - -.. code-block:: cpp - - concept RandomAccess2DImageConcept - { - typename x_coord_t = const_view_t::x_coord_t; - typename y_coord_t = const_view_t::y_coord_t; - - Img::Img(x_coord_t width, y_coord_t height, std::size_t alignment=0); - Img::Img(x_coord_t width, y_coord_t height, value_type fill_value, std::size_t alignment); - - x_coord_t Img::width() const; - y_coord_t Img::height() const; - - void Img::recreate(x_coord_t width, y_coord_t height, std::size_t alignment=1); - void Img::recreate(x_coord_t width, y_coord_t height, value_type fill_value, std::size_t alignment); - }; - -GIL images have views that model ``ImageViewConcept`` and operate on pixels. - -.. code-block:: cpp - - concept ImageConcept - { - where MutableImageViewConcept; - typename coord_t = view_t::coord_t; - }; - -Images, unlike locators and image views, don't have 'mutable' set of concepts -because immutable images are not very useful. - -.. seealso:: - - - `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 ------- - -GIL provides a class, ``image``, which is templated over the value type -(the pixel) and models ``ImageConcept``: - -.. code-block:: cpp - - template - < - typename Pixel, // Models PixelValueConcept - bool IsPlanar, // planar or interleaved image - typename A=std::allocator - > - class image; - -The image constructor takes an alignment parameter which allows for -constructing images that are word-aligned or 8-byte aligned. The alignment is -specified in bytes. The default value for alignment is 0, which means there is -no padding at the end of rows. Many operations are faster using such -1D-traversable images, because ``image_view::x_iterator`` can be used to -traverse the pixels, instead of the more complicated ``image_view::iterator``. -Note that when alignment is 0, packed images are aligned to the bit - i.e. -there are no padding bits at the end of rows of packed images. diff --git a/doc/design/image_view.rst b/doc/design/image_view.rst deleted file mode 100644 index 9a82c30c89..0000000000 --- a/doc/design/image_view.rst +++ /dev/null @@ -1,433 +0,0 @@ -Image View -========== - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -An image view is a generalization of STL range concept to multiple dimensions. -Similar to ranges (and iterators), image views are shallow, don't own the -underlying data and don't propagate their constness over the data. -For example, a constant image view cannot be resized, but may allow modifying -the pixels. For pixel-immutable operations, use constant-value image view -(also called non-mutable image view). Most general N-dimensional views satisfy -the following concept: - -.. code-block:: cpp - - concept RandomAccessNDImageViewConcept - { - typename value_type; // for pixel-based views, the pixel type - typename reference; // result of dereferencing - typename difference_type; // result of operator-(iterator,iterator) (1-dimensional!) - typename const_t; where RandomAccessNDImageViewConcept; // same as View, but over immutable values - typename point_t; where PointNDConcept; // N-dimensional point - typename locator; where RandomAccessNDLocatorConcept; // N-dimensional locator. - typename iterator; where RandomAccessTraversalConcept; // 1-dimensional iterator over all values - typename reverse_iterator; where RandomAccessTraversalConcept; - typename size_type; // the return value of size() - - // Equivalent to RandomAccessNDLocatorConcept::axis - template struct axis { - typename coord_t = point_t::axis::coord_t; - typename iterator; where RandomAccessTraversalConcept; // iterator along D-th axis. - where SameType; - where SameType; - }; - - // Defines the type of a view similar to this type, except it invokes Deref upon dereferencing - template struct add_deref { - typename type; where RandomAccessNDImageViewConcept; - static type make(const View& v, const Deref& deref); - }; - - static const size_t num_dimensions = point_t::num_dimensions; - - // Create from a locator at the top-left corner and dimensions - View::View(const locator&, const point_type&); - - size_type View::size() const; // total number of elements - reference operator[](View, const difference_type&) const; // 1-dimensional reference - iterator View::begin() const; - iterator View::end() const; - reverse_iterator View::rbegin() const; - reverse_iterator View::rend() const; - iterator View::at(const point_t&); - point_t View::dimensions() const; // number of elements along each dimension - bool View::is_1d_traversable() const; // Does an iterator over the first dimension visit each value? - - // iterator along a given dimension starting at a given point - template View::axis::iterator View::axis_iterator(const point_t&) const; - - reference operator()(View,const point_t&) const; - }; - - concept MutableRandomAccessNDImageViewConcept - { - where Mutable; - }; - -Two-dimensional image views have the following extra requirements: - -.. code-block:: cpp - - concept RandomAccess2DImageViewConcept - { - where num_dimensions==2; - - typename x_iterator = axis<0>::iterator; - typename y_iterator = axis<1>::iterator; - typename x_coord_t = axis<0>::coord_t; - typename y_coord_t = axis<1>::coord_t; - typename xy_locator = locator; - - x_coord_t View::width() const; - y_coord_t View::height() const; - - // X-navigation - x_iterator View::x_at(const point_t&) 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::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; - - // (x,y) versions of all methods taking point_t - View::View(x_coord_t,y_coord_t,const locator&); - iterator View::at(x_coord_t,y_coord_t) const; - reference operator()(View,x_coord_t,y_coord_t) const; - xy_locator View::xy_at(x_coord_t,y_coord_t) const; - x_iterator View::x_at(x_coord_t,y_coord_t) const; - y_iterator View::y_at(x_coord_t,y_coord_t) const; - }; - - concept MutableRandomAccess2DImageViewConcept - : MutableRandomAccessNDImageViewConcept {}; - -Image views that GIL typically uses operate on value types that model -``PixelValueConcept`` and have some additional requirements: - -.. code-block:: cpp - - concept ImageViewConcept - { - where PixelValueConcept; - where PixelIteratorConcept; - where PixelIteratorConcept; - where x_coord_t == y_coord_t; - - typename coord_t = x_coord_t; - - std::size_t View::num_channels() const; - }; - - - concept MutableImageViewConcept - : MutableRandomAccess2DImageViewConcept - {}; - -Two image views are compatible if they have compatible pixels and the same -number of dimensions: - -.. code-block:: cpp - - concept ViewsCompatibleConcept - { - where PixelsCompatibleConcept; - where V1::num_dimensions == V2::num_dimensions; - }; - -Compatible views must also have the same dimensions (i.e. the same width and -height). Many algorithms taking multiple views require that they be pairwise -compatible. - -.. seealso:: - - - `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 ------- - -GIL provides a model for ``ImageViewConcept`` called ``image_view``. It is -templated over a model of ``PixelLocatorConcept``. (If instantiated with a -model of ``MutablePixelLocatorConcept``, it models -``MutableImageViewConcept``.) Synopsis: - -.. code-block:: cpp - - // Locator models PixelLocatorConcept, could be MutablePixelLocatorConcept - template - class image_view - { - public: - typedef Locator xy_locator; - typedef iterator_from_2d iterator; - ... - private: - xy_locator _pixels; // 2D pixel locator at the top left corner of the image view range - point_t _dimensions; // width and height - }; - -Image views are lightweight objects. A regular interleaved view is typically -16 bytes long - two integers for the width and height (inside dimensions), one -for the number of bytes between adjacent rows (inside the locator) and one -pointer to the beginning of the pixel block. - -Algorithms ----------- - -GIL provides algorithms constructing views from raw data or other views. - -Creating Views from Raw Pixels -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Standard image views can be constructed from raw data of any supported color -space, bit depth, channel ordering or planar vs. interleaved structure. -Interleaved views are constructed using ``interleaved_view``, supplying the -image dimensions, number of bytes per row, and a pointer to the first pixel: - -.. code-block:: cpp - - // Iterator models pixel iterator (e.g. rgb8_ptr_t or rgb8c_ptr_t) - template - image_view<...> interleaved_view(ptrdiff_t width, ptrdiff_t height, Iterator pixels, ptrdiff_t rowsize) - -Planar views are defined for every color space and take each plane separately. -Here is the RGB one: - -.. code-block:: cpp - - // Iterator models channel iterator (e.g. bits8* or bits8 const*) - template - image_view<...> planar_rgb_view( - ptrdiff_t width, ptrdiff_t height, - IC r, IC g, IC b, ptrdiff_t rowsize); - -Note that the supplied pixel/channel iterators could be constant (read-only), -in which case the returned view is a constant-value (immutable) view. - -Creating Image Views from Other Image Views -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -It is possible to construct one image view from another by changing some -policy of how image data is interpreted. The result could be a view whose type -is derived from the type of the source. GIL uses the following metafunctions -to get the derived types: - -.. code-block:: cpp - - // Some result view types - template - struct dynamic_xy_step_type : public dynamic_y_step_type::type> {}; - - template - struct dynamic_xy_step_transposed_type : public dynamic_xy_step_type::type> {}; - - // color and bit depth converted view to match pixel type P - template - struct color_converted_view_type - { - typedef ... type; // image view adaptor with value type DstP, over SrcView - }; - - // single-channel view of the N-th channel of a given view - template - struct nth_channel_view_type - { - typedef ... type; - }; - -GIL provides the following view transformations: - -.. code-block:: cpp - - // flipped upside-down, left-to-right, transposed view - template typename dynamic_y_step_type::type flipped_up_down_view(const View& src); - template typename dynamic_x_step_type::type flipped_left_right_view(const View& src); - template typename dynamic_xy_step_transposed_type::type transposed_view(const View& src); - - // rotations - template typename dynamic_xy_step_type::type rotated180_view(const View& src); - template typename dynamic_xy_step_transposed_type::type rotated90cw_view(const View& src); - template typename dynamic_xy_step_transposed_type::type rotated90ccw_view(const View& src); - - // view of an axis-aligned rectangular area within an image - template View subimage_view(const View& src, - const View::point_t& top_left, const View::point_t& dimensions); - - // subsampled view (skipping pixels in X and Y) - template typename dynamic_xy_step_type::type subsampled_view(const View& src, - const View::point_t& step); - - template - color_converted_view_type::type color_converted_view(const View& src); - template // with a custom color converter - color_converted_view_type::type color_converted_view(const View& src); - - template - nth_channel_view_type::view_t nth_channel_view(const View& view, int n); - -The implementations of most of these view factory methods are straightforward. -Here is, for example, how the flip views are implemented. The flip upside-down -view creates a view whose first pixel is the bottom left pixel of the original -view and whose y-step is the negated step of the source. - -.. code-block:: cpp - - template - typename dynamic_y_step_type::type flipped_up_down_view(const View& src) - { - gil_function_requires >(); - typedef typename dynamic_y_step_type::type RView; - return RView(src.dimensions(),typename RView::xy_locator(src.xy_at(0,src.height()-1),-1)); - } - -The call to ``gil_function_requires`` ensures (at compile time) that the -template parameter is a valid model of ``ImageViewConcept``. Using it -generates easier to track compile errors, creates no extra code and has no -run-time performance impact. We are using the ``boost::concept_check library``, -but wrapping it in ``gil_function_requires``, which performs the check if the -``BOOST_GIL_USE_CONCEPT_CHECK`` is set. It is unset by default, because there -is a significant increase in compile time when using concept checks. We will -skip ``gil_function_requires`` in the code examples in this guide for the sake -of succinctness. - -Image views can be freely composed (see section :doc:`metafunctions` for -explanation of the typedefs ``rgb16_image_t`` and ``gray16_step_view_t)``: - -.. code-block:: cpp - - rgb16_image_t img(100,100); // an RGB interleaved image - - // grayscale view over the green (index 1) channel of img - gray16_step_view_t green=nth_channel_view(view(img),1); - - // 50x50 view of the green channel of img, upside down and taking every other pixel in X and in Y - gray16_step_view_t ud_fud=flipped_up_down_view(subsampled_view(green,2,2)); - -As previously stated, image views are fast, constant-time, shallow views over -the pixel data. The above code does not copy any pixels; it operates on the -pixel data allocated when ``img`` was created. - -STL-Style Algorithms on Image Views -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Image views provide 1D iteration of their pixels via ``begin()`` and ``end()`` -methods, which makes it possible to use STL algorithms with them. However, -using nested loops over X and Y is in many cases more efficient. -The algorithms in this section resemble STL algorithms, but they abstract away -the nested loops and take views (as opposed to ranges) as input. - -.. code-block:: cpp - - // Equivalents of std::copy and std::uninitialized_copy - // where ImageViewConcept, MutableImageViewConcept, ViewsCompatibleConcept - template - void copy_pixels(const V1& src, const V2& dst); - template - void uninitialized_copy_pixels(const V1& src, const V2& dst); - - // Equivalents of std::fill and std::uninitialized_fill - // where MutableImageViewConcept, PixelConcept, PixelsCompatibleConcept - template - void fill_pixels(const V& dst, const Value& val); - template - void uninitialized_fill_pixels(const V& dst, const Value& val); - - // Equivalent of std::for_each - // where ImageViewConcept, boost::UnaryFunctionConcept - // where PixelsCompatibleConcept - template - F for_each_pixel(const V& view, F fun); - template - F for_each_pixel_position(const V& view, F fun); - - // Equivalent of std::generate - // where MutableImageViewConcept, boost::UnaryFunctionConcept - // where PixelsCompatibleConcept - template - void generate_pixels(const V& dst, F fun); - - // Equivalent of std::transform with one source - // where ImageViewConcept, MutableImageViewConcept - // where boost::UnaryFunctionConcept - // where PixelsCompatibleConcept - // where PixelsCompatibleConcept - template - F transform_pixels(const V1& src, const V2& dst, F fun); - template - F transform_pixel_positions(const V1& src, const V2& dst, F fun); - - // Equivalent of std::transform with two sources - // where ImageViewConcept, ImageViewConcept, MutableImageViewConcept - // where boost::BinaryFunctionConcept - // where PixelsCompatibleConcept - // where PixelsCompatibleConcept - // where PixelsCompatibleConcept - template - F transform_pixels(const V1& src1, const V2& src2, const V3& dst, F fun); - template - F transform_pixel_positions(const V1& src1, const V2& src2, const V3& dst, F fun); - - // Copies a view into another, color converting the pixels if needed, with the default or user-defined color converter - // where ImageViewConcept, MutableImageViewConcept - // V1::value_type must be convertible to V2::value_type. - template - void copy_and_convert_pixels(const V1& src, const V2& dst); - template - void copy_and_convert_pixels(const V1& src, const V2& dst, ColorConverter ccv); - - // Equivalent of std::equal - // where ImageViewConcept, ImageViewConcept, ViewsCompatibleConcept - template - bool equal_pixels(const V1& view1, const V2& view2); - -Algorithms that take multiple views require that they have the same -dimensions. ``for_each_pixel_position`` and ``transform_pixel_positions`` pass -pixel locators, as opposed to pixel references, to their function objects. -This allows for writing algorithms that use pixel neighbours, as the tutorial -demonstrates. - -Most of these algorithms check whether the image views are 1D-traversable. -A 1D-traversable image view has no gaps at the end of the rows. -In other words, if an x_iterator of that view is advanced past the last pixel -in a row it will move to the first pixel of the next row. When image views are -1D-traversable, the algorithms use a single loop and run more efficiently. -If one or more of the input views are not 1D-traversable, the algorithms -fall-back to an X-loop nested inside a Y-loop. - -The algorithms typically delegate the work to their corresponding STL -algorithms. For example, ``copy_pixels`` calls ``std::copy`` either for each -row, or, when the images are 1D-traversable, once for all pixels. - -In addition, overloads are sometimes provided for the STL algorithms. -For example, ``std::copy`` for planar iterators is overloaded to perform -``std::copy`` for each of the planes. ``std::copy`` over bitwise-copyable -pixels results in ``std::copy`` over unsigned char, which STL -implements via ``memmove``. - -As a result ``copy_pixels`` may result in a single call to ``memmove`` for -interleaved 1D-traversable views, or one per each plane of planar -1D-traversable views, or one per each row of interleaved non-1D-traversable -images, etc. - -GIL also provides some beta-versions of image processing algorithms, such as -resampling and convolution in a numerics extension available on -http://stlab.adobe.com/gil/download.html. This code is in early stage of -development and is not optimized for speed diff --git a/doc/design/index.rst b/doc/design/index.rst deleted file mode 100644 index 0b76eb34a5..0000000000 --- a/doc/design/index.rst +++ /dev/null @@ -1,27 +0,0 @@ -Design Guide -============ - -The sections listed below are dedicated to describe the design, -structure and basic elements of the Generic Image Library (GIL). - -.. toctree:: - :maxdepth: 1 - :caption: Table of Contents - - basics - concepts - point - channel - color_space - color_base - pixel - pixel_iterator - pixel_locator - image_view - image - dynamic_image - metafunctions - examples - technicalities - extending - conclusions diff --git a/doc/design/metafunctions.rst b/doc/design/metafunctions.rst deleted file mode 100644 index 92d6a03a09..0000000000 --- a/doc/design/metafunctions.rst +++ /dev/null @@ -1,242 +0,0 @@ -Metafunctions -============= - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -Flexibility comes at a price. GIL types can be very long and hard to read. -To address this problem, GIL provides typedefs to refer to any standard image, -pixel iterator, pixel locator, pixel reference or pixel value. - -They follow this pattern:: - - *ColorSpace* + *BitDepth* + ["s|f"] + ["c"] + ["_planar"] + ["_step"] + *ClassType* + "_t" - -where *ColorSpace* also indicates the ordering of components. - -Examples are ``rgb``, ``bgr``, ``cmyk``, ``rgba``. *BitDepth* can be, for -example, ``8``, ``16``, ``32``. By default the bits are unsigned integral type. -Append ``s`` to the bit depth to indicate signed integral, or ``f`` to -indicate floating point. ``c`` indicates object whose associated pixel -reference is immutable. ``_planar`` indicates planar organization (as opposed -to interleaved). ``_step`` indicates the type has a dynamic step and -*ClassType* is ``_image`` (image, using a standard allocator), ``_view`` -(image view), ``_loc`` (pixel locator), ``_ptr`` (pixel iterator), ``_ref`` -(pixel reference), ``_pixel`` (pixel value). - -Here are examples: - -.. code-block:: cpp - - bgr8_image_t i; // 8-bit unsigned (unsigned char) interleaved BGR image - cmyk16_pixel_t; x; // 16-bit unsigned (unsigned short) CMYK pixel value - cmyk16sc_planar_ref_t p(x); // const reference to a 16-bit signed integral (signed short) planar CMYK pixel x - rgb32f_planar_step_ptr_t ii; // step iterator to a floating point 32-bit (float) planar RGB pixel - -Homogeneous memory-based images -------------------------------- - -GIL provides the metafunctions that return the types of standard -homogeneous memory-based GIL constructs given a channel type, a -layout, and whether the construct is planar, has a step along the X -direction, and is mutable: - -.. code-block:: cpp - - template - struct pixel_reference_type { typedef ... type; }; - - template - struct pixel_value_type { typedef ... type; }; - - template - struct iterator_type { typedef ... type; }; - - template - struct locator_type { typedef ... type; }; - - template - struct view_type { typedef ... type; }; - - template > - struct image_type { typedef ... type; }; - - template > - struct packed_image_type { typedef ... type; }; - - template > - struct bit_aligned_image_type { typedef ... type; }; - -Packed and bit-aligned images ------------------------------ - -There are also helper metafunctions to construct packed and -bit-aligned images with up to five channels: - -.. code-block:: cpp - - template > - struct packed_image1_type { typedef ... type; }; - - template > - struct packed_image2_type { typedef ... type; }; - - template > - struct packed_image3_type { typedef ... type; }; - - template > - struct packed_image4_type { typedef ... type; }; - - template > - struct packed_image5_type { typedef ... type; }; - - template > - struct bit_aligned_image1_type { typedef ... type; }; - - template > - struct bit_aligned_image2_type { typedef ... type; }; - - template > - struct bit_aligned_image3_type { typedef ... type; }; - - template > - struct bit_aligned_image4_type { typedef ... type; }; - - template > - struct bit_aligned_image5_type { typedef ... type; }; - -Iterators and views -------------------- - -Here ``ChannelValue`` models ``ChannelValueConcept``. We don't need -``IsYStep`` because GIL's memory-based locator and view already allow -the vertical step to be specified dynamically. Iterators and views can -be constructed from a pixel type: - -.. code-block:: cpp - - template - struct iterator_type_from_pixel { typedef ... type; }; - - template - struct view_type_from_pixel { typedef ... type; }; - -Using a heterogeneous pixel type will result in heterogeneous iterators and -views. Types can also be constructed from horizontal iterator: - -.. code-block:: cpp - - template - struct type_from_x_iterator - { - typedef ... step_iterator_t; - typedef ... xy_locator_t; - typedef ... view_t; - }; - -Pixel components ----------------- - -You can get pixel-related types of any pixel-based GIL constructs (pixels, -iterators, locators and views) using the following metafunctions provided by -``PixelBasedConcept``, ``HomogeneousPixelBasedConcept`` and metafunctions -built on top of them: - -.. code-block:: cpp - - template struct color_space_type { typedef ... type; }; - template struct channel_mapping_type { typedef ... type; }; - template struct is_planar { typedef ... type; }; - - // Defined by homogeneous constructs - template struct channel_type { typedef ... type; }; - template struct num_channels { typedef ... type; }; - -Deriving and manipulating existing types ----------------------------------------- - -There are metafunctions to construct the type of a construct from an existing -type by changing one or more of its properties: - -.. code-block:: cpp - - template - struct derived_pixel_reference_type - { - typedef ... type; // Models PixelConcept - }; - - template - struct derived_iterator_type - { - typedef ... type; // Models PixelIteratorConcept - }; - - template - struct derived_view_type - { - typedef ... type; // Models ImageViewConcept - }; - - template - struct derived_image_type - { - typedef ... type; // Models ImageConcept - }; - -You can replace one or more of its properties and use ``boost::use_default`` -for the rest. In this case ``IsPlanar``, ``IsStep`` and ``IsMutable`` are -MPL boolean constants. For example, here is how to create the type of a view -just like ``View``, but being grayscale and planar: - -.. code-block:: cpp - - using VT = typename derived_view_type::type; - -Type traits ------------ - -These are metafunctions, some of which return integral types which can be -evaluated like this: - -.. code-block:: cpp - - static_assert(is_planar::value == true, ""); - -GIL also supports type analysis metafunctions of the form: - -.. code-block:: cpp - - [pixel_reference/iterator/locator/view/image] + "_is_" + [basic/mutable/step] - -For example: - -.. code-block:: cpp - - if (view_is_mutable::value) - { - ... - } - -A *basic* GIL construct is a memory-based construct that uses the built-in GIL -classes and does not have any function object to invoke upon dereferencing. -For example, a simple planar or interleaved, step or non-step RGB image view -is basic, but a color converted view or a virtual view is not. diff --git a/doc/design/pixel.rst b/doc/design/pixel.rst deleted file mode 100644 index 1f8478ff1c..0000000000 --- a/doc/design/pixel.rst +++ /dev/null @@ -1,285 +0,0 @@ -Pixel -===== - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -A pixel is a set of channels defining the color at a given point in an -image. Conceptually, a pixel is little more than a color base whose -elements model ``ChannelConcept``. All properties of pixels inherit -from color bases: pixels may be *homogeneous* if all of their channels -have the same type; otherwise they are called *heterogeneous*. The -channels of a pixel may be addressed using semantic or physical -indexing, or by color; all color-base algorithms work on pixels as -well. Two pixels are *compatible* if their color spaces are the same -and their channels, paired semantically, are compatible. Note that -constness, memory organization and reference/value are ignored. For -example, an 8-bit RGB planar reference is compatible to a constant -8-bit BGR interleaved pixel value. Most pairwise pixel operations -(copy construction, assignment, equality, etc.) are only defined for -compatible pixels. - -Pixels (as well as other GIL constructs built on pixels, such as -iterators, locators, views and images) must provide metafunctions to -access their color space, channel mapping, number of channels, and -(for homogeneous pixels) the channel type: - -.. code-block:: cpp - - concept PixelBasedConcept - { - typename color_space_type; - where Metafunction >; - where ColorSpaceConcept::type>; - typename channel_mapping_type; - where Metafunction >; - where ChannelMappingConcept::type>; - typename is_planar; - where Metafunction >; - where SameType::type, bool>; - }; - - concept HomogeneousPixelBasedConcept - { - typename channel_type; - where Metafunction >; - where ChannelConcept::type>; - }; - -Pixels model the following concepts: - -.. code-block:: cpp - - concept PixelConcept : ColorBaseConcept

, PixelBasedConcept

- { - where is_pixel

::value==true; - // where for each K [0..size

::value-1]: - // ChannelConcept >; - - typename value_type; where PixelValueConcept; - typename reference; where PixelConcept; - typename const_reference; where PixelConcept; - static const bool P::is_mutable; - - template where { PixelConcept } - P::P(P2); - template where { PixelConcept } - bool operator==(const P&, const P2&); - template where { PixelConcept } - bool operator!=(const P&, const P2&); - }; - - concept MutablePixelConcept : PixelConcept

, MutableColorBaseConcept

- { - where is_mutable==true; - }; - - concept HomogeneousPixelConcept : HomogeneousColorBaseConcept

, HomogeneousPixelBasedConcept

- { - P::template element_const_reference_type

::type operator[](P p, std::size_t i) const { return dynamic_at_c(P,i); } - }; - - concept MutableHomogeneousPixelConcept : MutableHomogeneousColorBaseConcept

- { - P::template element_reference_type

::type operator[](P p, std::size_t i) { return dynamic_at_c(p,i); } - }; - - concept PixelValueConcept : PixelConcept

, Regular

- { - where SameType; - }; - - concept PixelsCompatibleConcept : ColorBasesCompatibleConcept - { - // where for each K [0..size::value): - // ChannelsCompatibleConcept::type, kth_semantic_element_type::type>; - }; - -A pixel is *convertible* to a second pixel if it is possible to -approximate its color in the form of the second pixel. Conversion is -an explicit, non-symmetric and often lossy operation (due to both -channel and color space approximation). Convertibility requires -modeling the following concept: - -.. code-block:: cpp - - template - concept PixelConvertibleConcept - { - void color_convert(const SrcPixel&, DstPixel&); - }; - -The distinction between ``PixelConcept`` and ``PixelValueConcept`` is -analogous to that for channels and color bases - pixel reference proxies model -both, but only pixel values model the latter. - -.. seealso:: - - - `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 ------- - -The most commonly used pixel is a homogeneous pixel whose values are -together in memory. For this purpose GIL provides the struct -``pixel``, templated over the channel value and layout: - -.. code-block:: cpp - - // models HomogeneousPixelValueConcept - template struct pixel; - - // Those typedefs are already provided by GIL - typedef pixel rgb8_pixel_t; - typedef pixel bgr8_pixel_t; - - bgr8_pixel_t bgr8(255,0,0); // pixels can be initialized with the channels directly - rgb8_pixel_t rgb8(bgr8); // compatible pixels can also be copy-constructed - - rgb8 = bgr8; // assignment and equality is defined between compatible pixels - assert(rgb8 == bgr8); // assignment and equality operate on the semantic channels - - // The first physical channels of the two pixels are different - assert(at_c<0>(rgb8) != at_c<0>(bgr8)); - assert(dynamic_at_c(bgr8,0) != dynamic_at_c(rgb8,0)); - assert(rgb8[0] != bgr8[0]); // same as above (but operator[] is defined for pixels only) - -Planar pixels have their channels distributed in memory. While they share the -same value type (``pixel``) with interleaved pixels, their reference type is a -proxy class containing references to each of the channels. -This is implemented with the struct ``planar_pixel_reference``: - -.. code-block:: cpp - - // models HomogeneousPixel - template struct planar_pixel_reference; - - // Define the type of a mutable and read-only reference. (These typedefs are already provided by GIL) - typedef planar_pixel_reference< bits8&,rgb_t> rgb8_planar_ref_t; - typedef planar_pixel_reference rgb8c_planar_ref_t; - -Note that, unlike the ``pixel`` struct, planar pixel references are templated -over the color space, not over the pixel layout. They always use a canonical -channel ordering. Ordering of their elements is unnecessary because their -elements are references to the channels. - -Sometimes the channels of a pixel may not be byte-aligned. For example an RGB -pixel in '5-5-6' format is a 16-bit pixel whose red, green and blue channels -occupy bits [0..4],[5..9] and [10..15] respectively. GIL provides a model for -such packed pixel formats: - -.. code-block:: cpp - - // define an rgb565 pixel - 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; - - function_requires >(); - - // rgb565 is compatible with bgr556. - function_requires >(); - -In some cases, the pixel itself may not be byte aligned. For example, -consider an RGB pixel in '2-3-2' format. Its size is 7 bits. GIL -refers to such pixels, pixel iterators and images as -"bit-aligned". Bit-aligned pixels (and images) are more complex than -packed ones. Since packed pixels are byte-aligned, we can use a C++ -reference as the reference type to a packed pixel, and a C pointer as -an x_iterator over a row of packed pixels. For bit-aligned constructs -we need a special reference proxy class (bit_aligned_pixel_reference) -and iterator class (bit_aligned_pixel_iterator). The value type of -bit-aligned pixels is a packed_pixel. Here is how to use bit_aligned -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; - - // A mutable iterator over BGR232 pixels - typedef bit_aligned_pixel_iterator bgr232_ptr_t; - - // BGR232 pixel value. It is a packed_pixel of size 1 byte. (The last bit is unused) - typedef std::iterator_traits::value_type bgr232_pixel_t; - static_assert(sizeof(bgr232_pixel_t) == 1, ""); - - bgr232_pixel_t red(0,0,3); // = 0RRGGGBB, = 01100000 = 0x60 - - // a buffer of 7 bytes fits exactly 8 BGR232 pixels. - unsigned char pix_buffer[7]; - std::fill(pix_buffer,pix_buffer+7,0); - - // Fill the 8 pixels with red - bgr232_ptr_t pix_it(&pix_buffer[0],0); // start at bit 0 of the first pixel - for (int i=0; i<8; ++i) - { - *pix_it++ = red; - } - // Result: 0x60 0x30 0x11 0x0C 0x06 0x83 0xC1 - -Algorithms ----------- - -Since pixels model ``ColorBaseConcept`` and ``PixelBasedConcept`` all -algorithms and metafunctions of color bases can work with them as well: - -.. code-block:: cpp - - // This is how to access the first semantic channel (red) - assert(semantic_at_c<0>(rgb8) == semantic_at_c<0>(bgr8)); - - // This is how to access the red channel by name - assert(get_color(rgb8) == get_color(bgr8)); - - // This is another way of doing it (some compilers don't like the first one) - assert(get_color(rgb8,red_t()) == get_color(bgr8,red_t())); - - // This is how to use the PixelBasedConcept metafunctions - 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, mp11::mp_list_c > )); - - // Pixels contain just the three channels and nothing extra - BOOST_MPL_ASSERT(sizeof(rgb8_pixel_t)==3); - - rgb8_planar_ref_t ref(bgr8); // copy construction is allowed from a compatible mutable pixel type - - get_color(ref) = 10; // assignment is ok because the reference is mutable - assert(get_color(bgr8)==10); // references modify the value they are bound to - - // Create a zero packed pixel and a full regular unpacked pixel. - rgb565_pixel_t r565; - rgb8_pixel_t rgb_full(255,255,255); - - // Convert all channels of the unpacked pixel to the packed one & assert the packed one is full - get_color(r565,red_t()) = channel_convert(get_color(rgb_full,red_t())); - get_color(r565,green_t()) = channel_convert(get_color(rgb_full,green_t())); - get_color(r565,blue_t()) = channel_convert(get_color(rgb_full,blue_t())); - assert(r565 == rgb565_pixel_t((uint16_t)65535)); - -GIL also provides the ``color_convert`` algorithm to convert between pixels of -different color spaces and channel types: - -.. code-block:: cpp - - rgb8_pixel_t red_in_rgb8(255,0,0); - cmyk16_pixel_t red_in_cmyk16; - color_convert(red_in_rgb8,red_in_cmyk16); diff --git a/doc/design/pixel_iterator.rst b/doc/design/pixel_iterator.rst deleted file mode 100644 index 1bd2c73232..0000000000 --- a/doc/design/pixel_iterator.rst +++ /dev/null @@ -1,294 +0,0 @@ -Pixel Iterator -============== - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -Pixel iterators are random traversal iterators whose ``value_type -models`` ``PixelValueConcept``. - -Fundamental Iterator --------------------- - -Pixel iterators provide metafunctions to determine whether they are mutable -(i.e. whether they allow for modifying the pixel they refer to), to get the -immutable (read-only) type of the iterator, and to determine whether they are -plain iterators or adaptors over another pixel iterator: - -.. code-block:: cpp - - concept PixelIteratorConcept - : PixelBasedConcept - { - where PixelValueConcept; - typename const_iterator_type::type; - where PixelIteratorConcept::type>; - static const bool iterator_is_mutable::value; - static const bool is_iterator_adaptor::value; // is it an iterator adaptor - }; - - template - concept MutablePixelIteratorConcept : PixelIteratorConcept, MutableRandomAccessIteratorConcept {}; - -.. seealso:: - - - `PixelIteratorConcept <../reference/group___pixel_iterator_concept_pixel_iterator.html>`_ - - `MutablePixelIteratorConcept <../reference/structboost_1_1gil_1_1_mutable_pixel_iterator_concept.html>`_ - -Models -^^^^^^ - -A built-in pointer to pixel, ``pixel*``, is GIL model for -pixel iterator over interleaved homogeneous pixels. Similarly, -``packed_pixel*`` is GIL model for an iterator -over interleaved packed pixels. - -For planar homogeneous pixels, GIL provides the class -``planar_pixel_iterator``, templated over a channel iterator and color space. -Here is how the standard mutable and read-only planar RGB iterators over -unsigned char are defined: - -.. code-block:: cpp - - template - struct planar_pixel_iterator; - - // GIL provided typedefs - typedef planar_pixel_iterator rgb8c_planar_ptr_t; - typedef planar_pixel_iterator< bits8*, rgb_t> rgb8_planar_ptr_t; - -``planar_pixel_iterator`` also models ``HomogeneousColorBaseConcept`` (it -subclasses from ``homogeneous_color_base``) and, as a result, all color base -algorithms apply to it. The element type of its color base is a channel -iterator. For example, GIL implements ``operator++`` of planar iterators -approximately like this: - -.. code-block:: cpp - - template - struct inc : public std::unary_function - { - T operator()(T x) const { return ++x; } - }; - - template - planar_pixel_iterator& - planar_pixel_iterator::operator++() - { - static_transform(*this,*this,inc()); - return *this; - } - -Since ``static_transform`` uses compile-time recursion, incrementing an -instance of ``rgb8_planar_ptr_t`` amounts to three pointer increments. -GIL also uses the class ``bit_aligned_pixel_iterator`` as a model for a pixel -iterator over bit-aligned pixels. Internally it keeps track of the current -byte and the bit offset. - -Iterator Adaptor ----------------- - -Iterator adaptor is an iterator that wraps around another iterator. Its -``is_iterator_adaptor`` metafunction must evaluate to true, and it needs to -provide a member method to return the base iterator, a metafunction to get its -type, and a metafunction to rebind to another base iterator: - -.. code-block:: cpp - - concept IteratorAdaptorConcept - { - where SameType::type, mp11::mp_true>; - - typename iterator_adaptor_get_base; - where Metafunction >; - where boost_concepts::ForwardTraversalConcept::type>; - - typename another_iterator; - typename iterator_adaptor_rebind::type; - where boost_concepts::ForwardTraversalConcept; - where IteratorAdaptorConcept::type>; - - const iterator_adaptor_get_base::type& Iterator::base() const; - }; - - template - concept MutableIteratorAdaptorConcept : IteratorAdaptorConcept {}; - -.. seealso:: - - - `IteratorAdaptorConcept <../reference/structboost_1_1gil_1_1_iterator_adaptor_concept.html>`_ - - `MutableIteratorAdaptorConcept <../reference/structboost_1_1gil_1_1_mutable_iterator_adaptor_concept.html>`_ - -Models -^^^^^^ - -GIL provides several models of ``IteratorAdaptorConcept``: - -- ``memory_based_step_iterator``: An iterator adaptor that changes - the fundamental step of the base iterator - (see :ref:`design/pixel_iterator:Step Iterator`) - -- ``dereference_iterator_adaptor``: An iterator that applies a - unary function ``Fn`` upon dereferencing. It is used, for example, for - on-the-fly color conversion. It can be used to construct a shallow image - "view" that pretends to have a different color space or channel depth. - See :doc:`image_view` for more. The unary function ``Fn`` must - model ``PixelDereferenceAdaptorConcept`` (see below). - -Pixel Dereference Adaptor -------------------------- - -Pixel dereference adaptor is a unary function that can be applied upon -dereferencing a pixel iterator. Its argument type could be anything (usually a -``PixelConcept``) and the result type must be convertible to ``PixelConcept``: - -.. code-block:: cpp - - template - concept PixelDereferenceAdaptorConcept: - DefaultConstructibleConcept, - CopyConstructibleConcept, - AssignableConcept - { - typename const_t; where PixelDereferenceAdaptorConcept; - typename value_type; where PixelValueConcept; - typename reference; where PixelConcept::type>; // may be mutable - typename const_reference; // must not be mutable - static const bool D::is_mutable; - - where Convertible; - }; - -Models -^^^^^^ - -GIL provides several models of ``PixelDereferenceAdaptorConcept``: - -* ``color_convert_deref_fn``: a function object that performs color conversion - -* ``detail::nth_channel_deref_fn``: a function object that returns a grayscale - pixel corresponding to the n-th channel of a given pixel - -* ``deref_compose``: a function object that composes two models of - ``PixelDereferenceAdaptorConcept``. Similar to ``std::unary_compose``, - except it needs to pull the additional typedefs required by - ``PixelDereferenceAdaptorConcept`` - -GIL uses pixel dereference adaptors to implement image views that perform -color conversion upon dereferencing, or that return the N-th channel of the -underlying pixel. They can be used to model virtual image views that perform -an arbitrary function upon dereferencing, for example a view of the Mandelbrot -set. ``dereference_iterator_adaptor`` is an iterator wrapper over -a pixel iterator ``Iterator`` that invokes the given dereference iterator -adaptor ``Fn`` upon dereferencing. - -Step Iterator -------------- - -Sometimes we want to traverse pixels with a unit step other than the one -provided by the fundamental pixel iterators. Examples where this would be -useful: - -* a single-channel view of the red channel of an RGB interleaved image -* left-to-right flipped image (step = -fundamental_step) -* subsampled view, taking every N-th pixel (step = N*fundamental_step) -* traversal in vertical direction (step = number of bytes per row) -* any combination of the above (steps are multiplied) - -Step iterators are forward traversal iterators that allow changing the step -between adjacent values: - -.. code-block:: cpp - - concept StepIteratorConcept - { - template void Iterator::set_step(D step); - }; - - concept MutableStepIteratorConcept - : StepIteratorConcept - {}; - -GIL currently provides a step iterator whose ``value_type models`` -``PixelValueConcept``. In addition, the step is specified in memory units -(which are bytes or bits). This is necessary, for example, when implementing -an iterator navigating along a column of pixels - the size of a row of pixels -may sometimes not be divisible by the size of a pixel; for example rows may be -word-aligned. - -To advance in bytes/bits, the base iterator must model -``MemoryBasedIteratorConcept``. A memory-based iterator has an inherent memory -unit, which is either a bit or a byte. It must supply functions returning the -number of bits per memory unit (1 or 8), the current step in memory units, the -memory-unit distance between two iterators, and a reference a given distance -in memunits away. It must also supply a function that advances an iterator a -given distance in memory units. ``memunit_advanced`` and -``memunit_advanced_ref`` have a default implementation but some iterators may -supply a more efficient version: - -.. code-block:: cpp - - concept MemoryBasedIteratorConcept - < - boost_concepts::RandomAccessTraversalConcept Iterator - > - { - typename byte_to_memunit; where metafunction >; - std::ptrdiff_t memunit_step(const Iterator&); - std::ptrdiff_t memunit_distance(const Iterator& , const Iterator&); - void memunit_advance(Iterator&, std::ptrdiff_t diff); - Iterator memunit_advanced(const Iterator& p, std::ptrdiff_t diff) { Iterator tmp; memunit_advance(tmp,diff); return tmp; } - Iterator::reference memunit_advanced_ref(const Iterator& p, std::ptrdiff_t diff) { return *memunit_advanced(p,diff); } - }; - -It is useful to be able to construct a step iterator over another iterator. -More generally, given a type, we want to be able to construct an equivalent -type that allows for dynamically specified horizontal step: - -.. code-block:: cpp - - concept HasDynamicXStepTypeConcept - { - typename dynamic_x_step_type; - where Metafunction >; - }; - -All models of pixel iterators, locators and image views that GIL provides -support ``HasDynamicXStepTypeConcept``. - -.. seealso:: - - - `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 -^^^^^^ - -All standard memory-based iterators GIL currently provides model -``MemoryBasedIteratorConcept``. GIL provides the class -``memory_based_step_iterator`` which models ``PixelIteratorConcept``, -``StepIteratorConcept``, and ``MemoryBasedIteratorConcept``. It takes the base -iterator as a template parameter (which must model ``PixelIteratorConcept`` -and ``MemoryBasedIteratorConcept``) and allows changing the step dynamically. -GIL implementation contains the base iterator and a ``ptrdiff_t`` denoting the -number of memory units (bytes or bits) to skip for a unit step. It may also be -used with a negative number. GIL provides a function to create a step iterator -from a base iterator and a step: - -.. code-block:: cpp - - // Iterator models MemoryBasedIteratorConcept, HasDynamicXStepTypeConcept - template - typename dynamic_x_step_type::type make_step_iterator(Iterator const& it, std::ptrdiff_t step); - -GIL also provides a model of an iterator over a virtual array of pixels, -``position_iterator``. It is a step iterator that keeps track of the pixel -position and invokes a function object to get the value of the pixel upon -dereferencing. It models ``PixelIteratorConcept`` and ``StepIteratorConcept`` -but not ``MemoryBasedIteratorConcept``. diff --git a/doc/design/pixel_locator.rst b/doc/design/pixel_locator.rst deleted file mode 100644 index 30eb263c4a..0000000000 --- a/doc/design/pixel_locator.rst +++ /dev/null @@ -1,303 +0,0 @@ -Pixel Locator -============= - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -A Locator allows for navigation in two or more dimensions. Locators are -N-dimensional iterators in spirit, but we use a different name because they -don't satisfy all the requirements of iterators. For example, they don't -supply increment and decrement operators because it is unclear which dimension -the operators should advance along. -N-dimensional locators model the following concept: - -.. code-block:: cpp - - concept RandomAccessNDLocatorConcept - { - typename value_type; // value over which the locator navigates - typename reference; // result of dereferencing - typename difference_type; where PointNDConcept; // return value of operator-. - typename const_t; // same as Loc, but operating over immutable values - typename cached_location_t; // type to store relative location (for efficient repeated access) - typename point_t = difference_type; - - static const size_t num_dimensions; // dimensionality of the locator - where num_dimensions = point_t::num_dimensions; - - // The difference_type and iterator type along each dimension. The iterators may only differ in - // difference_type. Their value_type must be the same as Loc::value_type - template struct axis { - typename coord_t = point_t::axis::coord_t; - typename iterator; where RandomAccessTraversalConcept; // iterator along D-th axis. - where iterator::value_type == value_type; - }; - - // Defines the type of a locator similar to this type, except it invokes Deref upon dereferencing - template struct add_deref { - typename type; where RandomAccessNDLocatorConcept; - static type make(const Loc& loc, const Deref& deref); - }; - - Loc& operator+=(Loc&, const difference_type&); - Loc& operator-=(Loc&, const difference_type&); - Loc operator+(const Loc&, const difference_type&); - Loc operator-(const Loc&, const difference_type&); - - reference operator*(const Loc&); - reference operator[](const Loc&, const difference_type&); - - // Storing relative location for faster repeated access and accessing it - cached_location_t Loc::cache_location(const difference_type&) const; - reference operator[](const Loc&,const cached_location_t&); - - // Accessing iterators along a given dimension at the current location or at a given offset - template axis::iterator& Loc::axis_iterator(); - template axis::iterator const& Loc::axis_iterator() const; - template axis::iterator Loc::axis_iterator(const difference_type&) const; - }; - - template - concept MutableRandomAccessNDLocatorConcept - : RandomAccessNDLocatorConcept - { - where Mutable; - }; - -Two-dimensional locators have additional requirements: - -.. code-block:: cpp - - concept RandomAccess2DLocatorConcept - { - where num_dimensions==2; - where Point2DConcept; - - typename x_iterator = axis<0>::iterator; - typename y_iterator = axis<1>::iterator; - typename x_coord_t = axis<0>::coord_t; - typename y_coord_t = axis<1>::coord_t; - - // Only available to locators that have dynamic step in Y - //Loc::Loc(const Loc& loc, y_coord_t); - - // Only available to locators that have dynamic step in X and Y - //Loc::Loc(const Loc& loc, x_coord_t, y_coord_t, bool transposed=false); - - x_iterator& Loc::x(); - x_iterator const& Loc::x() const; - y_iterator& Loc::y(); - y_iterator const& Loc::y() const; - - x_iterator Loc::x_at(const difference_type&) const; - y_iterator Loc::y_at(const difference_type&) const; - Loc Loc::xy_at(const difference_type&) const; - - // x/y versions of all methods that can take difference type - x_iterator Loc::x_at(x_coord_t, y_coord_t) const; - y_iterator Loc::y_at(x_coord_t, y_coord_t) const; - Loc Loc::xy_at(x_coord_t, y_coord_t) const; - reference operator()(const Loc&, x_coord_t, y_coord_t); - cached_location_t Loc::cache_location(x_coord_t, y_coord_t) const; - - bool Loc::is_1d_traversable(x_coord_t width) const; - y_coord_t Loc::y_distance_to(const Loc& loc2, x_coord_t x_diff) const; - }; - - concept MutableRandomAccess2DLocatorConcept - : MutableRandomAccessNDLocatorConcept {}; - -2D locators can have a dynamic step not just horizontally, but -vertically. This gives rise to the Y equivalent of -``HasDynamicXStepTypeConcept``: - -.. code-block:: cpp - - concept HasDynamicYStepTypeConcept - { - typename dynamic_y_step_type; - where Metafunction >; - }; - -All locators and image views that GIL provides model -``HasDynamicYStepTypeConcept``. - -Sometimes it is necessary to swap the meaning of X and Y for a given locator -or image view type (for example, GIL provides a function to transpose an image -view). Such locators and views must be transposable: - -.. code-block:: cpp - - concept HasTransposedTypeConcept - { - typename transposed_type; - where Metafunction >; - }; - -All GIL provided locators and views model ``HasTransposedTypeConcept``. - -The locators GIL uses operate over models of ``PixelConcept`` and their x and -y dimension types are the same. They model the following concept: - -.. code-block:: cpp - - concept PixelLocatorConcept - { - where PixelValueConcept; - where PixelIteratorConcept; - where PixelIteratorConcept; - where x_coord_t == y_coord_t; - - typename coord_t = x_coord_t; - }; - - concept MutablePixelLocatorConcept : MutableRandomAccess2DLocatorConcept {}; - -.. seealso:: - - - `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 ------- - -GIL provides two models of ``PixelLocatorConcept`` - a memory-based locator, -``memory_based_2d_locator`` and a virtual locator ``virtual_2d_locator``. - -The ``memory_based_2d_locator`` is a locator over planar or interleaved images -that have their pixels in memory. It takes a model of ``StepIteratorConcept`` -over pixels as a template parameter. (When instantiated with a model of -``MutableStepIteratorConcept``, it models ``MutablePixelLocatorConcept``.) - -.. code-block:: cpp - - // StepIterator models StepIteratorConcept, MemoryBasedIteratorConcept - template - class memory_based_2d_locator; - -The step of ``StepIterator`` must be the number of memory units (bytes or -bits) per row (thus it must be memunit advanceable). The class -``memory_based_2d_locator`` is a wrapper around ``StepIterator`` and uses it -to navigate vertically, while its base iterator is used to navigate -horizontally. - -Combining fundamental iterator and step iterator allows us to create locators -that describe complex pixel memory organizations. First, we have a choice of -iterator to use for horizontal direction, i.e. for iterating over the pixels -on the same row. Using the fundamental and step iterators gives us four -choices: - -- ``pixel*`` - for interleaved images -- ``planar_pixel_iterator`` - for planar images -- ``memory_based_step_iterator*>`` - for interleaved images with - non-standard step -- ``memory_based_step_iterator >`` - for planar - images with non-standard step - -Of course, one could provide their own custom x-iterator. One such example -described later is an iterator adaptor that performs color conversion when -dereferenced. - -Given a horizontal iterator ``XIterator``, we could choose the ``y-iterator``, -the iterator that moves along a column, as -``memory_based_step_iterator`` with a step equal to the number of -memory units (bytes or bits) per row. Again, one is free to provide their own -y-iterator. - -Then we can instantiate -``memory_based_2d_locator >`` to obtain -a 2D pixel locator, as the diagram indicates: - -.. image:: ../images/pixel_locator.gif - -The ``memory_based_2d_locator`` also offers `cached_location_t` as mechanism -to store relative locations for optimized repeated access of neighborhood -pixels. The 2D coordinates of relative locations are cached as 1-dimensional -raw byte offsets. This provides efficient access if neighboring locations -relative to a given locator are read or written frequently (e.g. in filters). - -The ``virtual_2d_locator`` is a locator that is instantiated with a function -object invoked upon dereferencing a pixel. It returns the value of a pixel -given its X,Y coordinates. Virtual locators can be used to implement virtual -image views that can model any user-defined function. See the GIL tutorial for -an example of using virtual locators to create a view of the Mandelbrot set. - -Both the virtual and the memory-based locators subclass from -``pixel_2d_locator_base``, a base class that provides most of the interface -required by ``PixelLocatorConcept``. Users may find this base class useful if -they need to provide other models of ``PixelLocatorConcept``. - -Here is some sample code using locators: - -.. code-block:: cpp - - loc=img.xy_at(10,10); // start at pixel (x=10,y=10) - above=loc.cache_location(0,-1); // remember relative locations of neighbors above and below - below=loc.cache_location(0, 1); - ++loc.x(); // move to (11,10) - loc.y()+=15; // move to (11,25) - loc-=point(1,1);// move to (10,24) - *loc=(loc(0,-1)+loc(0,1))/2; // set pixel (10,24) to the average of (10,23) and (10,25) (grayscale pixels only) - *loc=(loc[above]+loc[below])/2; // the same, but faster using cached relative neighbor locations - -The standard GIL locators are fast and lightweight objects. For example, the -locator for a simple interleaved image consists of one raw pointer to the -pixel location plus one integer for the row size in bytes, for a total of -8 bytes. ``++loc.x()`` amounts to incrementing a raw pointer (or N pointers -for planar images). Computing 2D offsets is slower as it requires -multiplication and addition. Filters, for example, need to access the same -neighbors for every pixel in the image, in which case the relative positions -can be cached into a raw byte difference using ``cache_location``. -In the above example ``loc[above]`` for simple interleaved images amounts to a -raw array index operator. - -Iterator over 2D image ----------------------- - -Sometimes we want to perform the same, location-independent operation -over all pixels of an image. In such a case it is useful to represent -the pixels as a one-dimensional array. GIL's ``iterator_from_2d`` is a -random access traversal iterator that visits all pixels in an image in -the natural memory-friendly order left-to-right inside -top-to-bottom. It takes a locator, the width of the image and the -current X position. This is sufficient information for it to determine -when to do a "carriage return". Synopsis: - -.. code-block:: cpp - - template // Models PixelLocatorConcept - class iterator_from_2d - { - public: - iterator_from_2d(const Locator& loc, int x, int width); - - iterator_from_2d& operator++(); // if (++_x<_width) ++_p.x(); else _p+=point_t(-_width,1); - - ... - private: - int _x, _width; - Locator _p; - }; - -Iterating through the pixels in an image using ``iterator_from_2d`` is slower -than going through all rows and using the x-iterator at each row. This is -because two comparisons are done per iteration step - one for the end -condition of the loop using the iterators, and one inside -``iterator_from_2d::operator++`` to determine whether we are at the end of a -row. For fast operations, such as pixel copy, this second check adds about -15% performance delay (measured for interleaved images on Intel platform). -GIL overrides some STL algorithms, such as ``std::copy`` and ``std::fill``, -when invoked with ``iterator_from_2d``-s, to go through each row using their -base x-iterators, and, if the image has no padding (i.e. -``iterator_from_2d::is_1d_traversable()`` returns true), to simply iterate -using the x-iterators directly. diff --git a/doc/design/point.rst b/doc/design/point.rst deleted file mode 100644 index 28158ab2db..0000000000 --- a/doc/design/point.rst +++ /dev/null @@ -1,56 +0,0 @@ -Point -===== - -.. contents:: - :local: - :depth: 2 - -Overview --------- - -A point defines the location of a pixel inside an image. It can also be used -to describe the dimensions of an image. In most general terms, points are -N-dimensional and model the following concept: - -.. code-block:: cpp - - concept PointNDConcept : Regular - { - // the type of a coordinate along each axis - template struct axis; where Metafunction; - - const size_t num_dimensions; - - // accessor/modifier of the value of each axis. - template const typename axis::type& T::axis_value() const; - template typename axis::type& T::axis_value(); - }; - -GIL uses a two-dimensional point, which is a refinement of ``PointNDConcept`` -in which both dimensions are of the same type: - -.. code-block:: cpp - - concept Point2DConcept : PointNDConcept - { - where num_dimensions == 2; - where SameType::type, axis<1>::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 x,y; - }; - -.. seealso:: - - - `PointNDConcept <../reference/structboost_1_1gil_1_1_point_n_d_concept.html>`_ - - `Point2DConcept <../reference/structboost_1_1gil_1_1_point2_d_concept.html>`_ - -Models ------- - -GIL provides a model of ``Point2DConcept``, ``point`` where ``T`` is the -coordinate type. diff --git a/doc/design/technicalities.rst b/doc/design/technicalities.rst deleted file mode 100644 index 39fa4cb423..0000000000 --- a/doc/design/technicalities.rst +++ /dev/null @@ -1,89 +0,0 @@ -Technicalities -============== - -.. contents:: - :local: - :depth: 2 - -Creating a reference proxy --------------------------- - -Sometimes it is necessary to create a proxy class that represents a -reference to a given object. Examples of these are GIL's reference to -a planar pixel (``planar_pixel_reference``) and GIL's sub-byte channel -references. Writing a reference proxy class can be tricky. One problem -is that the proxy reference is constructed as a temporary object and -returned by value upon dereferencing the iterator: - -.. code-block:: cpp - - struct rgb_planar_pixel_iterator - { - typedef my_reference_proxy reference; - reference operator*() const { return reference(red,green,blue); } - }; - -The problem arises when an iterator is dereferenced directly into a -function that takes a mutable pixel: - -.. code-block:: cpp - - template // Models MutablePixelConcept - void invert_pixel(Pixel& p); - - rgb_planar_pixel_iterator myIt; - invert_pixel(*myIt); // compile error! - -C++ does not allow for matching a temporary object against a non-constant -reference. The solution is to: - -* Use const qualifier on all members of the reference proxy object: - -.. code-block:: cpp - - template - struct my_reference_proxy - { - const my_reference_proxy& operator=(const my_reference_proxy& p) const; - const my_reference_proxy* operator->() const { return this; } - ... - }; - -* Use different classes to denote mutable and constant reference - (maybe based on the constness of the template parameter) - -* Define the reference type of your iterator with const qualifier: - -.. code-block:: cpp - - struct iterator_traits - { - typedef const my_reference_proxy reference; - }; - -A second important issue is providing an overload for ``swap`` for -your reference class. The default ``std::swap`` will not work -correctly. You must use a real value type as the temporary. A further -complication is that in some implementations of the STL the ``swap`` -function is incorrectly called qualified, as ``std::swap``. The only -way for these STL algorithms to use your overload is if you define it -in the ``std`` namespace: - -.. code-block:: cpp - - namespace std - { - template - void swap(my_reference_proxy& x, my_reference_proxy& y) - { - my_value tmp=x; - x=y; - y=tmp; - } - } - -Lastly, remember that constructors and copy-constructors of proxy -references are always shallow and assignment operators are deep. - -We are grateful to Dave Abrahams, Sean Parent and Alex Stepanov for -suggesting the above solution. diff --git a/doc/histogram/create.rst b/doc/histogram/create.rst deleted file mode 100644 index 7df551c63e..0000000000 --- a/doc/histogram/create.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _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/extend.rst b/doc/histogram/extend.rst deleted file mode 100644 index 4a9393791a..0000000000 --- a/doc/histogram/extend.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. _extend_support: - -Extending the class -=================== - -.. contents:: - :local: - :depth: 1 - -User defined Axes ------------------ - -In case you need a histogram with an axis of an arbitrary type that is not identified by -the C++ Standard Library, you need to provide an 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 let's 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 deleted file mode 100644 index cf3f913c6a..0000000000 --- a/doc/histogram/fill.rst +++ /dev/null @@ -1,103 +0,0 @@ -.. _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 deleted file mode 100644 index ff8cc16bba..0000000000 --- a/doc/histogram/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index 819d308f6e..0000000000 --- a/doc/histogram/limitations.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _limitations: - -Limitations -=========== - -*TODO* \ No newline at end of file diff --git a/doc/histogram/overview.rst b/doc/histogram/overview.rst deleted file mode 100644 index cb2d045601..0000000000 --- a/doc/histogram/overview.rst +++ /dev/null @@ -1,28 +0,0 @@ -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 deleted file mode 100644 index 9b932a2cef..0000000000 --- a/doc/histogram/stl_compatibility.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _stl_compatibility: - -STL compatibility -================= - -*TODO* \ No newline at end of file diff --git a/doc/histogram/subhistogram.rst b/doc/histogram/subhistogram.rst deleted file mode 100644 index 4e046a45a2..0000000000 --- a/doc/histogram/subhistogram.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. _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 deleted file mode 100644 index 5c4bf77554..0000000000 --- a/doc/histogram/utilities.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _utilities: - -Utilities -========= - -*TODO* \ No newline at end of file diff --git a/doc/image_processing/affine-region-detectors.rst b/doc/image_processing/affine-region-detectors.rst deleted file mode 100644 index ab7d1ccc72..0000000000 --- a/doc/image_processing/affine-region-detectors.rst +++ /dev/null @@ -1,115 +0,0 @@ -Affine region detectors ------------------------ - -What is being detected? -~~~~~~~~~~~~~~~~~~~~~~~ - -Affine region is basically any region of the image -that is stable under affine transformations. It can be -edges under affinity conditions, corners (small patch of an image) -or any other stable features. - --------------- - -Available detectors -~~~~~~~~~~~~~~~~~~~ - -At the moment, the following detectors are implemented - -- Harris detector - -- Hessian detector - --------------- - -Algorithm steps -~~~~~~~~~~~~~~~ - -Harris and Hessian -^^^^^^^^^^^^^^^^^^ - -Both are derived from a concept called Moravec window. Let's have a look -at the image below: - -.. figure:: ./Moravec-window-corner.png - :alt: Moravec window corner case - - Moravec window corner case - -As can be noticed, moving the yellow window in any direction will cause -very big change in intensity. Now, let's have a look at the edge case: - -.. figure:: ./Moravec-window-edge.png - :alt: Moravec window edge case - - Moravec window edge case - -In this case, intensity change will happen only when moving in -particular direction. - -This is the key concept in understanding how the two corner detectors -work. - -The algorithms have the same structure: - -1. Compute image derivatives - -2. Compute weighted sum - -3. Compute response - -4. Threshold (optional) - -Harris and Hessian differ in what **derivatives they compute**. Harris -computes the following derivatives: - -``HarrisMatrix = [(dx)^2, dxdy], [dxdy, (dy)^2]`` - -(note that ``d(x^2)`` and ``(dy^2)`` are **numerical** powers, not gradient again). - -The three distinct terms of a matrix can be separated into three images, -to simplify implementation. Hessian, on the other hand, computes second -order derivatives: - -``HessianMatrix = [dxdx, dxdy][dxdy, dydy]`` - -**Weighted sum** is the same for both. Usually Gaussian blur -matrix is used as weights, because corners should have hill like -curvature in gradients, and other weights might be noisy. -Basically overlay weights matrix over a corner, compute sum of -``s[i,j]=image[x + i, y + j] * weights[i, j]`` for ``i, j`` -from zero to weight matrix dimensions, then move the window -and compute again until all of the image is covered. - -**Response computation** is a matter of choice. Given the general form -of both matrices above - -``[a, b][c, d]`` - -One of the response functions is - -``response = det - k * trace^2 = a * c - b * d - k * (a + d)^2`` - -``k`` is called discrimination constant. Usual values are ``0.04`` - -``0.06``. - -The other is simply determinant - -``response = det = a * c - b * d`` - -**Thresholding** is optional, but without it the result will be -extremely noisy. For complex images, like the ones of outdoors, for -Harris it will be in order of 100000000 and for Hessian will be in order -of 10000. For simpler images values in order of 100s and 1000s should be -enough. The numbers assume ``uint8_t`` gray image. - -To get a deeper explanation please refer to the following **papers**: - -`Harris, Christopher G., and Mike Stephens. "A combined corner and edge -detector." In Alvey vision conference, vol. 15, no. 50, pp. 10-5244. -1988. `__ - -`Mikolajczyk, Krystian, and Cordelia Schmid. "An affine invariant interest point detector." In European conference on computer vision, pp. 128-142. Springer, Berlin, Heidelberg, 2002. `__ - -`Mikolajczyk, Krystian, Tinne Tuytelaars, Cordelia Schmid, Andrew Zisserman, Jiri Matas, Frederik Schaffalitzky, Timor Kadir, and Luc Van Gool. "A comparison of affine region detectors." International journal of computer vision 65, no. 1-2 (2005): 43-72. `__ - diff --git a/doc/image_processing/contrast_enhancement/histogram_equalization.rst b/doc/image_processing/contrast_enhancement/histogram_equalization.rst deleted file mode 100644 index 2a52118c66..0000000000 --- a/doc/image_processing/contrast_enhancement/histogram_equalization.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. _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:: https://github.com/boost-gil/test-images/blob/master/jpeg/suite/barbara.jpg - :width: 512px - :align: center - :height: 256px - :alt: Could not load image. - :figclass: align-center - -**RGB** - -.. figure:: https://github.com/boost-gil/test-images/blob/master/jpeg/suite/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/doc/image_processing/contrast_enhancement/histogram_matching.rst b/doc/image_processing/contrast_enhancement/histogram_matching.rst deleted file mode 100644 index 0f89a9420c..0000000000 --- a/doc/image_processing/contrast_enhancement/histogram_matching.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. _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:: https://github.com/boost-gil/test-images/blob/master/jpeg/suite/matching.jpg - :width: 600px - :align: center - :height: 300px - :alt: Could not load image. - :figclass: align-center - -**Histogram matched Image** - -.. figure:: https://github.com/boost-gil/test-images/blob/master/jpeg/suite/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/index.rst b/doc/image_processing/index.rst deleted file mode 100644 index c5bc61cfad..0000000000 --- a/doc/image_processing/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -Image Processing -================ - -The GIL documentation sections listed below are dedicated to describe the library -features, structures and algorithms, for image processing and analysis. - -.. toctree:: - :maxdepth: 2 - :caption: Table of Contents - - overview - basics - affine-region-detectors - contrast_enhancement/index - diff --git a/doc/images/gil.png b/doc/images/gil.png deleted file mode 100644 index 2540c4ca07772271403401452070cb7ac0a50044..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21385 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%0A8<))E)vs(pE)mmd z9pt*z!2=Z~D5a8d|1U!)>9Ig>Tg1Ct@%|`O|TH!JM!K(S3zuHOJv#(KE zltq`#iOcJYX|Ks5{^h_a7UZN;mXpqlkFVz+-ui&zukL1EMheEhL5ea`Sy&v~$Zit- zt_N1}K|>eM|MK_z>|{M^nS}OU2i`!4x4!Wh>t~QL>N|TG81~|D4D-lK`{S%hqR}W? zg^ZWKw3*yA4dwxtptqlGG2#gX`KKp0@xoj0^Q-DlkcyLOHrp}iRP1|po8ZdbV2ow= zOgzN9zoLW_b*)rA{|3dGDKwhwNW~)3HA*gA>*m$>YWT^nhdJMB!awX~UHNn*EsIGm z!4Nw(SF*e)YtF5|J1Soa ztXd!(`n*@yN_l<;rP*VT>Rq-$e(~Wa)VAF~C6};yc`@7XU4mXSW^h{5YR2sgaNmT3 zpJuZiQ@@LQ%JSkC@x=}cdym!8)9)lFP0J&z7VyZb1%f@)pxevE4hzLusT5_5Ri#Cv zaO7eu1%?!s&&!O9h<|bN5*IrxsO2&?Ei2;54NFN;E2cGr4TQp6?y}|pxkxRs^ zoS(xJ>lP!Gi21?0=kWT2eD@0*g)E!uhECq8X(1^oMry@bX{>$m=RCh{E&sfIqcF~( z+sD2S>Zxw%B1t4-b!iSeK3_?0x^~93&^tKHRikaRoO?24l4O<_W)ccVY3S*rswA5X zor)%t9qW*r4T}nS^P|hWTiZfXQW6^$7xL7`<-(>G2!*-WVL>WRW?5lo+`;tFh>w3e zc$y0xy`*atY* z&F3Ebqnl~{$&&PJ#5-F7%k7Z#Y$Ti@=mh362Hjrvp145eyeuAAF<;2;@CQS@eDDl! zykF1JZ-0@>;_Sp^0sBukuQ(~6>;8fcE_DW+zBKw7ZC_W z*mv?03aNzhd;^&}6`@F!-<)pX-`_dIvs+i+^#mP>MsbezH2Lo1H>H2` zBitiCKDWGty - - - - - Automatic redirection failed, please go to - html/index.html - - diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 40779419c7..0000000000 --- a/doc/index.rst +++ /dev/null @@ -1,67 +0,0 @@ -Boost Generic Image Library -=========================== - -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. - -Quickstart ----------- - -.. toctree:: - :maxdepth: 1 - - installation - tutorial/video - tutorial/histogram - tutorial/gradient - naming - -Core Library Documentation --------------------------- - -.. toctree:: - :maxdepth: 2 - - design/index - image_processing/index - histogram/index - API Reference <./reference/index.html#://> - -Extensions Documentation ------------------------- - -.. toctree:: - :maxdepth: 2 - - io - toolbox - numeric - histogram/extension/index - -Examples --------- - -* :download:`x_gradient.cpp <../example/x_gradient.cpp>`: - Writing an algorithm that operates on generic images -* :download:`dynamic_image.cpp <../example/dynamic_image.cpp>`: - Using images whose properties (color space, channel type) are specified - at run time -* :download:`histogram.cpp <../example/histogram.cpp>`: Creating a histogram -* :download:`interleaved_ptr.cpp <../example/interleaved_ptr.cpp>`, - :download:`interleaved_ptr.hpp <../example/interleaved_ptr.hpp>`, - :download:`interleaved_ref.hpp <../example/interleaved_ref.hpp>`: - Creating your own pixel reference and pixel iterator -* :download:`mandelbrot.cpp <../example/mandelbrot.cpp>`: - Creating a synthetic image defined by a function -* :download:`packed_pixel.cpp <../example/packed_pixel.cpp>`: - Defining bitmasks and images whose channels or pixels are not byte-aligned -* :download:`resize.cpp <../example/resize.cpp>`: - Rescaling an image using bilinear sampling (requires the optional - Numeric extension) -* :download:`affine.cpp <../example/affine.cpp>`: - Applying an affine transformation to an image (requires the optional - Numeric extension) -* :download:`convolution.cpp <../example/convolution.cpp>`: - Blurring images (requires the optional Numeric extension) diff --git a/doc/installation.rst b/doc/installation.rst deleted file mode 100644 index 716cfc7e1d..0000000000 --- a/doc/installation.rst +++ /dev/null @@ -1,32 +0,0 @@ -Installation -============ - -The latest version of Boost.GIL can be downloaded from https://github.com/boostorg/gil. - -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. - -.. note:: - - The exception to dependencies-free rule of GIL is the I/O extension - which requires client libraries implementing popular image formats - like libpng, libjpeg, etc. - -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++14 support. - -.. note:: - - Boost.GIL requires C++14 compiler since Boost 1.80. - -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. diff --git a/doc/io.rst b/doc/io.rst deleted file mode 100644 index 9dded96713..0000000000 --- a/doc/io.rst +++ /dev/null @@ -1,654 +0,0 @@ -IO extensions -============= - -Overview --------- - -This extension to boost::gil provides an easy to use interface for reading and -writing various image formats. It also includes a framework for adding -new formats. - -Please see section 3.3 for all supported image formats. A basic tutorial is -provided in section [link gil.io.tutorial Tutorial]. -Also, this extension requires Boost version 1.42 and up. -Furthermore the GIL extension Toolbox is used. - -For adding new image formats please refer to section -[link gil.io.using_io.extending_gil__io_with_new_formats Extending GIL::IO with new Formats]. - -Supported Platforms -------------------- - -All platforms supported by Boost which have a decent C++ compiler. -Depending on the image format one or more of the following image -libraries might be needed: - -* libtiff -* libjpeg -* libpng -* libraw -* zlib - -The library is designed to support as many formats as required by the user. -For instance, if the user only needs bmp support none of the above mentioned -dependencies are required. - -There are more details available in this documentation on the image format -dependencies. Please see section -[link gil.io.using_io.supported_image_formats Supported Image Formats]. - -Tutorial --------- - -Thanks to modern C++ programming techniques the interface for this library -is rather small and easy to use. In this tutorial I'll give you a short -walk-around on how to use this boost::gil extension. -For more details please refer to section 3. - -For each supported IO format a single top-level header file is provided. -For instance, include `boost/gil/extension/io/tiff.hpp` to be able -to read or write TIFF files. - -Reading An Image -~~~~~~~~~~~~~~~~ - -Probably the most common case to read a tiff image can be done as follows:: - - std::string filename( "image.tif" ); - rgb8_image_t img; - read_image( filename, img, tiff_tag() ); - -The code would be same for all other image formats. The only thing that needs -to change is the tag type ( tiff_tag ) in the read_image call. -The read_image() expects the supplied image type to be compatible with the -image stored in the file. If the user doesn't know what format an image has she -can use read_and_convert_image(). -Another important fact is that read_image() will allocate the appropriate -memory needed for the read operation. There are ``read_view`` or -``read_and_convert_view`` counterparts, if the memory is already allocated. - -Sometimes the user only wants to read a sub-part of an image, -then the above call would look as follows:: - - read_image( filename - , img - , image_read_settings< tiff_tag >( point_t( 0, 0 ), point_t( 50, 50 ) ) - ); - -The image_read_settings class will provide the user with image format -independent reading setting but can also serve as a pointer for format -dependent settings. -Please see the specific image format sections -[link gil.io.using_io.supported_image_formats Supported Image Formats] -for more details. - -Writing An Image -~~~~~~~~~~~~~~~~ - -Besides reading the information also writing is the second part of this -Boost.GIL extension. Writing is a lot simpler than reading since an existing -image view contains all the information. - -For instance writing an image can be done as follows:: - - std::string filename( "image.tif" ); - rgb8_image_t img( 640, 480 ); - - // write data into image - - write_view( filename - , view( img ) - , tiff_tag() - ); - - -The interface is similar to reading an image. To add image format specific -parameter the user can use ``image_write_info`` class. -For instance, a user can specify the JPEG quality when writing like this:: - - std::string filename( "image.jpg" ); - rgb8_image_t img( 640, 480 ); - - // write data into image - - write_view( filename - , view( img ) - , image_write_info< jpeg_tag >( 95 ) - ); - - -The above example will write an image where the jpeg quality is -set to 95 percent. - -Reading And Writing In-Memory Buffers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Reading and writing in-memory buffers are supported as well. See as follows:: - - // 1. Read an image. - ifstream in( "test.tif", ios::binary ); - - rgb8_image_t img; - read_image( in, img, tiff_tag() ); - - // 2. Write image to in-memory buffer. - stringstream out_buffer( ios_base::out | ios_base::binary ); - - rgb8_image_t src; - write_view( out_buffer, view( src ), tiff_tag() ); - - // 3. Copy in-memory buffer to another. - stringstream in_buffer( ios_base::in | ios_base::binary ); - in_buffer << out_buffer.rdbuf(); - - // 4. Read in-memory buffer to gil image - rgb8_image_t dst; - read_image( in_buffer, dst, tag_t() ); - - // 5. Write out image. - string filename( "out.tif" ); - ofstream out( filename.c_str(), ios_base::binary ); - write_view( out, view( dst ), tiff_tag() ); - -In case the user is using his own stream classes he has to make sure it -has the common interface read, write, seek, close, etc. interface. - -Using IO --------- - -General Overview -~~~~~~~~~~~~~~~~ - -The tutorial pointed out some use cases for reading and writing images in -various image formats. This section will provide a more thorough overview. - -The next sections will introduce the Read and Write interface. But it might be -worth pointing out that by using some advanced metaprogramming techniques the -interface is rather small and hopefully easy to understand. - -Besides the general interface the user also has the ability to interface -directly with the underlying image format. For that each reader or writer -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; - - backend_t backend = read_image_info( bmp_filename - , tag_t() - ); - - BOOST_CHECK_EQUAL( backend._info._width , 127 ); - BOOST_CHECK_EQUAL( backend._info._height, 64 ); - -Of course, the typedef can be removed when using c++11's auto feature. - -Read Interface -~~~~~~~~~~~~~~ - -As the Tutorial demonstrated there are a few ways to read images. -Here is an enumeration of all read functions with a short description: - -* ``read_image`` - read into a gil image with no conversion. - Memory is allocated. -* ``read_view`` - read into a gil view with no conversion. -* ``read_and_convert_image`` - read and convert into a gil image. - Memory is allocated. -* ``read_and_convert_view`` - read and convert into a gil view. -* ``read_image_info`` - read the image header. - -Conversion in this context is necessary if the source (file) has an -incompatible color space with the destination (gil image type). -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`` 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 -``read_xxx`` function. -The third and last parameter is either an instance of the -``image_read_settings`` or just the ``FormatTag``. -The settings can be various depending on the format which is being read. -But they all share settings for reading a partial image area. -The first point describes the top left image coordinate whereas the second -are the dimensions in x and y directions. - -Here is an example of setting up partial read:: - - read_image( filename - , img - , image_read_settings< tiff_tag >( point_t( 0, 0 ), point_t( 50, 50 ) ) - ); - -Each format supports reading just the header information, -using ``read_image_info``. Please refer to the format specific sections -under 3.3. A basic example follows:: - - image_read_info< tiff_t > info = read_image_info( filename - , tiff_t() - ); - -GIL also comes with a dynamic image extension. -In the context of GIL.IO a user can define an ``any_image`` type based on -several image types. The IO extension would then pick the matching image type -to the current image file. -The following example shows this feature:: - - any_image< gray8_image_t - , gray16_image_t - , rgb8_image_t - , rgba8_image_t - > runtime_image; - - read_image( filename - , runtime_image - , tiff_tag() - ); - -During the review it became clear that there is a need to read big images -scanline by scanline. To support such use case a ``scanline_reader`` is -implemented for all supported image formats. -The ``scanline_read_iterators`` will then allow to traverse through the image. -The following code sample shows the usage:: - - typedef tiff_tag tag_t; - - typedef scanline_reader< typename get_read_device< const char* - , tag_t - >::type - , tag_t - > reader_t; - - reader_t reader = make_scanline_reader( "C:/boost/libs/gil/test/extension/io/images/tiff/test.tif", tag_t() ); - - typedef rgba8_image_t image_t; - - image_t dst( reader._info._width, reader._info._height ); - fill_pixels( view(dst), image_t::value_type() ); - - typedef reader_t::iterator_t iterator_t; - - iterator_t it = reader.begin(); - iterator_t end = reader.end(); - - for( int row = 0; it != end; ++it, ++row ) - { - copy_pixels( interleaved_view( reader._info._width - , 1 - , ( image_t::view_t::x_iterator ) *it - , reader._scanline_length - ) - , subimage_view( view( dst ) - , 0 - , row - , reader._info._width - , 1 - ) - ); - } - -There are many ways to traverse an image but for as of now only by -scanline is supported. - - -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`` 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 a view object to image being written. -The third and last parameter is either a tag or an -``image_write_info`` object containing more settings. -One example for instance is the JPEG quality. -Refer to the format specific sections under 3.3. to have a list of all -the possible settings. - -Writing an any_image<...> is supported. See the following example:: - - any_image< gray8_image_t - , gray16_image_t - , rgb8_image_t - , rgba8_image_t - > runtime_image; - - // fill any_image - - write_view( filename - , view( runtime_image ) - , tiff_tag() - ); - -Compiler Symbols -~~~~~~~~~~~~~~~~ - -The following table gives an overview of all supported compiler symbols -that can be set by the user: - -.. comment [table Compiler Symbols - -======================================================== ======================================================== - Symbol Description -======================================================== ======================================================== -BOOST_GIL_IO_ENABLE_GRAY_ALPHA Enable the color space "gray_alpha". -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. -BOOST_GIL_IO_PNG_1_4_OR_LOWER Allow compiling with libpng 1.4 or lower. -BOOST_GIL_EXTENSION_IO_JPEG_C_LIB_COMPILED_AS_CPLUSPLUS libjpeg is compiled as c++ lib. -BOOST_GIL_EXTENSION_IO_PNG_C_LIB_COMPILED_AS_CPLUSPLUS libpng is compiled as c++ lib. -BOOST_GIL_EXTENSION_IO_TIFF_C_LIB_COMPILED_AS_CPLUSPLUS libtiff is compiled as c++ lib. -BOOST_GIL_EXTENSION_IO_ZLIB_C_LIB_COMPILED_AS_CPLUSPLUS zlib is compiled as c++ lib. -BOOST_GIL_IO_TEST_ALLOW_READING_IMAGES Allow basic test images to be read from local hard drive. The paths can be set in paths.hpp -BOOST_GIL_IO_TEST_ALLOW_WRITING_IMAGES Allow images to be written to the local hard drive. The paths can be set in paths.hpp -BOOST_GIL_IO_USE_BMP_TEST_SUITE_IMAGES Run tests using the bmp test images suite. See _BMP_TEST_FILES -BOOST_GIL_IO_USE_PNG_TEST_SUITE_IMAGES Run tests using the png test images suite. See _PNG_TEST_FILES -BOOST_GIL_IO_USE_PNM_TEST_SUITE_IMAGES Run tests using the pnm test images suite. Send me an email for accessing the files. -BOOST_GIL_IO_USE_TIFF_LIBTIFF_TEST_SUITE_IMAGES Run tests using the targa file format test images suite. See _TIFF_LIB_TIFF_TEST_FILES -BOOST_GIL_IO_USE_TIFF_GRAPHICSMAGICK_TEST_SUITE_IMAGES Run tests using the targa file format test images suite. See _TIFF_GRAPHICSMAGICK_TEST_FILES -======================================================== ======================================================== - -Supported Image Formats -~~~~~~~~~~~~~~~~~~~~~~~ - -BMP -+++ - -For a general overview of the BMP image file format go to the -following BMP_Wiki_. - -Please note, the code has not been tested on X Windows System variations -of the BMP format which are usually referred to as XBM and XPM formats. - -Here, only the MS Windows and OS/2 format is relevant. - -Currently the code is able to read and write the following image types: - -:Read: ``gray1_image_t``, ``gray4_image_t``, ``gray8_image_t``, ``rgb8_image_t`` and ``rgba8_image_t`` -:Write: ``rgb8_image_t`` and ``rgba8_image_t`` - -The lack of having an indexed image type in gil restricts the current -interface to only write out non-indexed images. -This is subject to change soon. - -JPEG -++++ - -For a general overview of the JPEG image file format go to the -following JPEG_Wiki_. - -This jpeg extension is based on the libjpeg library which can be -found here, JPEG_Lib_. - -All versions starting from 8x are supported. - -The user has to make sure this library is properly installed. -I strongly recommend the user to build the library yourself. -It could potentially save you a lot of trouble. - -Currently the code is able to read and write the following image types: - -:Read: ``gray8_image_t``, ``rgb8_image_t``, ``cmyk8_image_t`` -:Write: ``gray8_image_t``, ``rgb8_image_t``, ``cmyk8_image_t`` - -Reading YCbCr or YCCK images is possible but might result in inaccuracies since -both color spaces aren't available yet for gil. -For now these color spaces are read as rgb images. -This is subject to change soon. - -PNG -+++ - -For a general overview of the PNG image file format go to the -following PNG_Wiki_. - -This png extension is based on the libpng, which can be found -here, PNG_Lib_. - -All versions starting from 1.5.x are supported. - -The user has to make sure this library is properly installed. -I strongly recommend the user to build the library yourself. -It could potentially save you a lot of trouble. - -Currently the code is able to read and write the following image types: - -:Read: gray1, gray2, gray4, gray8, gray16, gray_alpha_8, gray_alpha_16, rgb8, rgb16, rgba8, rgba16 -:Write: gray1, gray2, gray4, gray8, gray16, gray_alpha_8, gray_alpha_16, rgb8, rgb16, rgba8, rgba16 - -For reading gray_alpha images the user has to compile application with ``BOOST_GIL_IO_ENABLE_GRAY_ALPHA`` -macro defined. This color space is defined in the toolbox by using ``gray_alpha.hpp``. - -PNM -+++ - -For a general overview of the PNM image file format go to the -following PNM_Wiki_. No external library is needed for the pnm format. - -The extension can read images in both flavours of the formats, ASCII and binary, -that is types from P1 through P6; can write only binary formats. - -Currently the code is able to read and write the following image types: - -:Read: gray1, gray8, rgb8 -:Write: gray1, gray8, rgb8 - -When reading a mono text image the data is read as a gray8 image. - -RAW -+++ - -For a general overview see RAW_Wiki_. - -Currently the extension is only able to read rgb8 images. - -TARGA -+++++ - -For a general overview of the BMP image file format go to the -following TARGA_Wiki_. - -Currently the code is able to read and write the following image types: - -:Read: rgb8_image_t and rgba8_image_t -:Write: rgb8_image_t and rgba8_image_t - -The lack of having an indexed image type in gil restricts the current -interface to only write out non-indexed images. -This is subject to change soon. - -TIFF -++++ - -For a general overview of the TIFF image file format go to the -following TIFF_Wiki_. - -This tiff extension is based on the libtiff, which can be found, TIFF_Lib_. - -All versions starting from 3.9.x are supported. - -The user has to make sure this library is properly installed. I strongly -recommend the user to build the library yourself. It could potentially -save you a lot of trouble. - -TIFF images can virtually encode all kinds of channel sizes representing -various color spaces. Even planar images are possible. -For instance, ``rbg323`` or ``gray7``. The channels also can have specific -formats, like integer values or floating point values. - -For a complete set of options please consult the following websites: - -* TIFF_Base_Tags_ -* TIFF_Extension_Tags_ - -The author of this extension is not claiming all tiff formats are supported. -This extension is likely to be a moving target adding new features with each -new milestone. Here is an incomplete list: - -* Multi-page TIFF - read only -* Strip TIFF - read and write support -* Tiled TIFF - read and write support with user defined tiled sizes -* Bit images TIFF - fully supported, like ``gray1_image_t`` (minisblack) -* Planar TIFF - fully supported -* Floating-point TIFF - fully supported -* Palette TIFF - supported but no indexed image type is available as of now - -This gil extension uses two different test image suites to test read and -write capabilities. See ``test_image`` folder. -It's advisable to use ImageMagick test viewer to display images. - - -Extending GIL::IO with new Formats -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Extending the gil::io with new formats is meant to be simple and -straightforward. Before adding I would recommend to have a look at existing -implementations and then trying to follow a couple of guidelines: - -* Create the following files for your new xxx format - * ``xxx_read.hpp`` - only includes read code - * ``xxx_write.hpp`` - only includes write code - * ``xxx_all.hpp`` - includes xxx_read.hpp and xxx_write.hpp -* Add the code to the ``boost::gil::detail`` namespace -* Create a tag type for the new format. Like this:: - - struct xxx_tag : format_tag {}; - -* Create the image_read_info for the new format. It contains all the - information that is necessary to read an image. It should be filled - and returned by the ``get_info`` member of the reader class. See below:: - - template<> struct image_read_info< xxx_tag > {}; - -* Create the image_write_info for the new format. It contains all the - information that is necessary to write an image:: - - template<> struct image_write_info< xxx_tag > {}; - -* Use the following reader skeleton as a start:: - - template< typename Device - , typename ConversionPolicy - > - class reader< Device - , xxx_tag - , ConversionPolicy - > - : public reader_base< xxx_tag - , ConversionPolicy - > - { - private: - - typedef typename ConversionPolicy::color_converter_type cc_t; - - public: - - reader( Device& device ) - : _io_dev( device ) - {} - - reader( Device& device - , const cc_t& cc - ) - : _io_dev( device ) - , reader_base< xxx_tag - , ConversionPolicy - >( cc ) - {} - - image_read_info< xxx_tag > get_info() - { - // your implementation here - } - - template< typename View > - void apply( const View& dst_view ) - { - // your implementation here - } - }; - -* The writer skeleton:: - - template< typename Device > - class writer< Device - , xxx_tag - > - { - public: - - writer( Device & file ) - : out(file) - {} - - template - void apply( const View& view ) - { - // your implementation here - } - - template - void apply( const View& view - , const image_write_info< xxx_tag >& info ) - { - // your implementation here - } - }; - -Running gil::io tests ---------------------- - -gil::io comes with a large suite of test cases which reads and writes various -file formats. It uses some test image suites which can be found online or -which can be demanded from me by sending me an email. - -There are some test images created by me in the test folder. -To enable unit tests which make use of them set the following compiler options -``BOOST_GIL_IO_TEST_ALLOW_READING_IMAGES`` and -``BOOST_GIL_IO_TEST_ALLOW_WRITING_IMAGES``. - -The following list provides all links to the image suites the compiler symbol -to enable the tests: - -:BMP: BMP_TEST_FILES_ -- BOOST_GIL_IO_USE_BMP_TEST_SUITE_IMAGES -:PNG: PNG_TEST_FILES_ -- BOOST_GIL_IO_USE_PNG_TEST_SUITE_IMAGES -:PNM: request files from me -- BOOST_GIL_IO_USE_PNM_TEST_SUITE_IMAGES -:TIFF: TIFF_LIB_TIFF_TEST_FILES_ -- BOOST_GIL_IO_USE_TIFF_LIBTIFF_TEST_SUITE_IMAGES -:TIFF: TIFF_GRAPHICSMAGICK_TEST_FILES_ -- BOOST_GIL_IO_USE_TIFF_GRAPHICSMAGICK_TEST_SUITE_IMAGES - - -.. _BMP_Wiki: http://en.wikipedia.org/wiki/BMP_file_format -.. _JPEG_Wiki: http://en.wikipedia.org/wiki/JPEG -.. _JPEG_lib: http://www.ijg.org/ -.. _PNG_Wiki: http://en.wikipedia.org/wiki/Portable_Network_Graphics -.. _PNG_Lib: http://libpng.org/pub/png/libpng.html -.. _PNM_Wiki: http://en.wikipedia.org/wiki/Portable_anymap -.. _RAW_Wiki: http://en.wikipedia.org/wiki/Raw_image_format -.. _TARGA_Wiki: http://en.wikipedia.org/wiki/Truevision_TGA -.. _RAW_lib: http://www.libraw.org/ -.. _RAW_Wiki: http://en.wikipedia.org/wiki/Raw_image_format -.. _TIFF_Wiki: http://en.wikipedia.org/wiki/Tagged_Image_File_Format -.. _TIFF_Lib: http://www.remotesensing.org/libtiff/ -.. _TIFF_Base_Tags: http://www.awaresystems.be/imaging/tiff/tifftags/baseline.html -.. _TIFF_Extension_Tags: http://www.awaresystems.be/imaging/tiff/tifftags/extension.html -.. _BMP_TEST_FILES: http://entropymine.com/jason/bmpsuite/ -.. _PNG_TEST_FILES: http://www.schaik.com/pngsuite/pngsuite.html -.. _TARGA_TEST_FILES: http://www.fileformat.info/format/tga/sample/index.htm -.. _TIFF_LIB_TIFF_TEST_FILES: http://www.remotesensing.org/libtiff/images.html -.. _TIFF_GRAPHICSMAGICK_TEST_FILES: ftp://ftp.graphicsmagick.org/pub/tiff-samples/tiff-sample-images-be.tar.gz diff --git a/doc/local-playbook.yml b/doc/local-playbook.yml new file mode 100644 index 0000000000..81b8fc7361 --- /dev/null +++ b/doc/local-playbook.yml @@ -0,0 +1,40 @@ +# +# Copyright (c) 2026 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) +# +# Official repository: https://github.com/boostorg/gil +# +# An Antora playbook used for local development and CI. +# The playbook includes Boost.GIL as its only component. + +site: + title: Boost.GIL + url: https://antora.cppalliance.org/develop/lib/doc + start_page: gil::index.adoc + robots: allow + keys: + repo_url: 'https://github.com/boostorg/gil' + +content: + sources: + - url: .. + start_path: doc + edit_url: 'https://github.com/boostorg/gil/edit/{refname}/{path}' + +ui: + bundle: + url: https://github.com/boostorg/website-v2-docs/releases/download/ui-master/ui-bundle.zip + snapshot: true + +asciidoc: + attributes: + page-pagination: '' + +output: + dir: build/site + +runtime: + log: + failure_level: error diff --git a/doc/image_processing/Moravec-window-corner.png b/doc/modules/ROOT/images/Moravec-window-corner.png similarity index 100% rename from doc/image_processing/Moravec-window-corner.png rename to doc/modules/ROOT/images/Moravec-window-corner.png diff --git a/doc/image_processing/Moravec-window-edge.png b/doc/modules/ROOT/images/Moravec-window-edge.png similarity index 100% rename from doc/image_processing/Moravec-window-edge.png rename to doc/modules/ROOT/images/Moravec-window-edge.png diff --git a/doc/_static/gil.png b/doc/modules/ROOT/images/gil.png similarity index 100% rename from doc/_static/gil.png rename to doc/modules/ROOT/images/gil.png diff --git a/doc/modules/ROOT/images/he_chart.png b/doc/modules/ROOT/images/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/images/interleaved.jpg b/doc/modules/ROOT/images/interleaved.jpg similarity index 100% rename from doc/images/interleaved.jpg rename to doc/modules/ROOT/images/interleaved.jpg diff --git a/doc/images/mandel.jpg b/doc/modules/ROOT/images/mandel.jpg similarity index 100% rename from doc/images/mandel.jpg rename to doc/modules/ROOT/images/mandel.jpg diff --git a/doc/images/monkey_steps.jpg b/doc/modules/ROOT/images/monkey_steps.jpg similarity index 100% rename from doc/images/monkey_steps.jpg rename to doc/modules/ROOT/images/monkey_steps.jpg diff --git a/doc/images/pixel_locator.gif b/doc/modules/ROOT/images/pixel_locator.gif similarity index 100% rename from doc/images/pixel_locator.gif rename to doc/modules/ROOT/images/pixel_locator.gif diff --git a/doc/images/planar.jpg b/doc/modules/ROOT/images/planar.jpg similarity index 100% rename from doc/images/planar.jpg rename to doc/modules/ROOT/images/planar.jpg diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc new file mode 100644 index 0000000000..2ed2ee2ed8 --- /dev/null +++ b/doc/modules/ROOT/nav.adoc @@ -0,0 +1,57 @@ +* xref:index.adoc[Introduction] +* Quickstart +** xref:installation.adoc[] +** xref:tutorial/video.adoc[] +** xref:tutorial/histogram.adoc[] +** xref:tutorial/gradient.adoc[] +** xref:naming.adoc[] +* Design Guide +** xref:design/index.adoc[] +** xref:design/basics.adoc[] +** xref:design/concepts.adoc[] +** xref:design/point.adoc[] +** xref:design/color_space.adoc[] +** xref:design/color_base.adoc[] +** xref:design/channel.adoc[] +** xref:design/pixel.adoc[] +** xref:design/pixel_iterator.adoc[] +** xref:design/pixel_locator.adoc[] +** xref:design/image_view.adoc[] +** xref:design/image.adoc[] +** xref:design/dynamic_image.adoc[] +** xref:design/metafunctions.adoc[] +** xref:design/examples.adoc[] +** xref:design/technicalities.adoc[] +** xref:design/extending.adoc[] +** xref:design/conclusions.adoc[] +* Image Processing +** xref:image_processing/index.adoc[] +** xref:image_processing/overview.adoc[] +** xref:image_processing/basics.adoc[] +** xref:image_processing/affine-region-detectors.adoc[] +** Contrast Enhancement +*** xref:image_processing/contrast_enhancement/index.adoc[] +*** xref:image_processing/contrast_enhancement/overview.adoc[] +*** xref:image_processing/contrast_enhancement/histogram_equalization.adoc[] +*** xref:image_processing/contrast_enhancement/histogram_matching.adoc[] +* Histogram +** xref:histogram/index.adoc[] +** xref:histogram/overview.adoc[] +** xref:histogram/create.adoc[] +** xref:histogram/fill.adoc[] +** xref:histogram/subhistogram.adoc[] +** xref:histogram/cumulative.adoc[] +** xref:histogram/stl_compatibility.adoc[] +** xref:histogram/utilities.adoc[] +** xref:histogram/extend.adoc[] +** xref:histogram/limitations.adoc[] +** Extension +*** xref:histogram/extension/index.adoc[] +*** xref:histogram/extension/overview.adoc[] +*** xref:histogram/extension/std.adoc[] +* Extensions +** xref:io.adoc[] +** xref:toolbox.adoc[] +** xref:numeric.adoc[] +* xref:examples.adoc[] +* xref:reference.adoc[] diff --git a/doc/design/basics.rst b/doc/modules/ROOT/pages/design/basics.adoc similarity index 81% rename from doc/design/basics.rst rename to doc/modules/ROOT/pages/design/basics.adoc index b5a4f85a45..92162c66c5 100644 --- a/doc/design/basics.rst +++ b/doc/modules/ROOT/pages/design/basics.adoc @@ -1,25 +1,24 @@ -Basics -====== += Basics Images are essential in any image processing, vision and video project, and yet the variability in image representations makes it difficult to write imaging algorithms that are both generic and efficient. In this section we will describe some of the challenges that we would like to address. -In the following discussion an *image* is a 2D array of pixels. A *pixel* is a +In the following discussion an _image_ is a 2D array of pixels. A _pixel_ is a set of color channels that represents the color at a given point in an image. -Each *channel* represents the value of a color component. There are two common -memory structures for an image. *Interleaved* images are represented by +Each _channel_ represents the value of a color component. There are two common +memory structures for an image. _Interleaved_ images are represented by grouping the pixels together in memory and interleaving all channels together, -whereas *planar* images keep the channels in separate color planes. Here is a +whereas _planar_ images keep the channels in separate color planes. Here is a 4x3 RGB image in which the second pixel of the first row is marked in red, in interleaved form: -.. image:: ../images/interleaved.jpg +image::interleaved.jpg[] and in planar form: -.. image:: ../images/planar.jpg +image::planar.jpg[] Note also that rows may optionally be aligned resulting in a potential padding at the end of rows. diff --git a/doc/modules/ROOT/pages/design/channel.adoc b/doc/modules/ROOT/pages/design/channel.adoc new file mode 100644 index 0000000000..41d6c13caa --- /dev/null +++ b/doc/modules/ROOT/pages/design/channel.adoc @@ -0,0 +1,209 @@ += Channel + +== Overview + +A channel indicates the intensity of a color component (for example, the red +channel in an RGB pixel). Typical channel operations are getting, comparing +and setting the channel values. Channels have associated minimum and maximum +value. GIL channels model the following concept: + +[source,cpp] +---- +concept ChannelConcept : EqualityComparable +{ + typename value_type = T; // use channel_traits::value_type to access it + where ChannelValueConcept; + typename reference = T&; // use channel_traits::reference to access it + typename pointer = T*; // use channel_traits::pointer to access it + typename const_reference = const T&; // use channel_traits::const_reference to access it + typename const_pointer = const T*; // use channel_traits::const_pointer to access it + static const bool is_mutable; // use channel_traits::is_mutable to access it + + static T min_value(); // use channel_traits::min_value to access it + static T max_value(); // use channel_traits::max_value to access it +}; + +concept MutableChannelConcept : Swappable, Assignable {}; + +concept ChannelValueConcept : Regular {}; +---- + +GIL allows built-in integral and floating point types to be channels. +Therefore the associated types and range information are defined in +`channel_traits` with the following default implementation: + +[source,cpp] +---- +template +struct channel_traits +{ + typedef T value_type; + typedef T& reference; + typedef T* pointer; + typedef T& const const_reference; + typedef T* const const_pointer; + + static value_type min_value() { return std::numeric_limits::min(); } + static value_type max_value() { return std::numeric_limits::max(); } +}; +---- + +Two channel types are _compatible_ if they have the same value type: + +[source,cpp] +---- +concept ChannelsCompatibleConcept +{ + where SameType; +}; +---- + +A channel may be _convertible_ to another channel: + +[source,cpp] +---- +template +concept ChannelConvertibleConcept +{ + Dst channel_convert(Src); +}; +---- + +Note that `ChannelConcept` and `MutableChannelConcept` do not require a +default constructor. Channels that also support default construction (and thus +are regular types) model `ChannelValueConcept`. +To understand the motivation for this distinction, consider a 16-bit RGB pixel +in a "565" bit pattern. Its channels correspond to bit ranges. To support such +channels, we need to create a custom proxy class corresponding to a reference +to a sub-byte channel. +Such a proxy reference class models only `ChannelConcept`, because, similar +to native C++ references, it may not have a default constructor. + +Note also that algorithms may impose additional requirements on channels, +such as support for arithmetic operations. + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_channel_concept.html[ChannelConcept] +* link:../reference/structboost_1_1gil_1_1_channel_value_concept.html[ChannelValueConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_channel_concept.html[MutableChannelConcept] +* link:../reference/structboost_1_1gil_1_1_channels_compatible_concept.html[ChannelsCompatibleConcept] +* link:../reference/structboost_1_1gil_1_1_channel_convertible_concept.html[ChannelConvertibleConcept] +==== + +== Models + +All C++11 fundamental integer and float point types are valid channels. + +The minimum and maximum values of a channel modeled by a built-in type +correspond to the minimum and maximum physical range of the built-in type, as +specified by its `std::numeric_limits`. Sometimes the physical range is not +appropriate. GIL provides `scoped_channel_value`, a model for a channel +adapter that allows for specifying a custom range. +We use it to define a `[0..1]` floating point channel type as follows: + +[source,cpp] +---- +struct float_zero { static float apply() { return 0.0f; } }; +struct float_one { static float apply() { return 1.0f; } }; +typedef scoped_channel_value bits32f; +---- + +GIL also provides models for channels corresponding to ranges of bits: + +[source,cpp] +---- +// Value of a channel defined over NumBits bits. Models ChannelValueConcept +template class packed_channel_value; + +// Reference to a channel defined over NumBits bits. Models ChannelConcept +template // true if the reference is mutable +class packed_channel_reference; + +// Reference to a channel defined over NumBits bits. Its FirstBit is a run-time parameter. Models ChannelConcept +template // true if the reference is mutable +class packed_dynamic_channel_reference; +---- + +Note that there are two models of a reference proxy which differ based on +whether the offset of the channel range is specified as a template or a +run-time parameter. The first model is faster and more compact while the +second model is more flexible. For example, the second model allows us to +construct an iterator over bit range channels. + +== Algorithms + +Here is how to construct the three channels of a 16-bit "565" pixel and set +them to their maximum value: + +[source,cpp] +---- +using channel16_0_5_reference_t = packed_channel_reference<0, 5, true>; +using channel16_5_6_reference_t = packed_channel_reference<5, 6, true>; +using channel16_11_5_reference_t = packed_channel_reference<11, 5, true>; + +std::uint16_t data=0; +channel16_0_5_reference_t channel1(&data); +channel16_5_6_reference_t channel2(&data); +channel16_11_5_reference_t channel3(&data); + +channel1 = channel_traits::max_value(); +channel2 = channel_traits::max_value(); +channel3 = channel_traits::max_value(); +assert(data == 65535); +---- + +Assignment, equality comparison and copy construction are defined only between +compatible channels: + +[source,cpp] +---- +packed_channel_value<5> channel_6bit = channel1; +channel_6bit = channel3; + +// compile error: Assignment between incompatible channels +//channel_6bit = channel2; +---- + +All channel models provided by GIL are pairwise convertible: + +[source,cpp] +---- +channel1 = channel_traits::max_value(); +assert(channel1 == 31); + +bits16 chan16 = channel_convert(channel1); +assert(chan16 == 65535); +---- + +Channel conversion is a lossy operation. GIL's channel conversion is a linear +transformation between the ranges of the source and destination channel. +It maps precisely the minimum to the minimum and the maximum to the maximum. +(For example, to convert from uint8_t to uint16_t GIL does not do a bit shift +because it will not properly match the maximum values. Instead GIL multiplies +the source by 257.) + +All channel models that GIL provides are convertible from/to an integral or +floating point type. Thus they support arithmetic operations. Here are the +channel-level algorithms that GIL provides: + +[source,cpp] +---- +// Converts a source channel value into a destination channel. +// Linearly maps the value of the source into the range of the destination. +template +typename channel_traits::value_type channel_convert(SrcChannel src); + +// returns max_value - x + min_value +template +typename channel_traits::value_type channel_invert(Channel x); + +// returns a * b / max_value +template +typename channel_traits::value_type channel_multiply(Channel a, Channel b); +---- diff --git a/doc/modules/ROOT/pages/design/color_base.adoc b/doc/modules/ROOT/pages/design/color_base.adoc new file mode 100644 index 0000000000..37988b7fc9 --- /dev/null +++ b/doc/modules/ROOT/pages/design/color_base.adoc @@ -0,0 +1,242 @@ += Color Base + +== Overview + +A color base is a container of color elements. The most common use of color +base is in the implementation of a pixel, in which case the color elements are +channel values. The color base concept, however, can be used in other +scenarios. For example, a planar pixel has channels that are not contiguous in +memory. Its reference is a proxy class that uses a color base whose elements +are channel references. Its iterator uses a color base whose elements are +channel iterators. + +Color base models must satisfy the following concepts: + +[source,cpp] +---- +concept ColorBaseConcept + : CopyConstructible, EqualityComparable +{ + // a GIL layout (the color space and element permutation) + typename layout_t; + + // The type of K-th element + template struct kth_element_type; + where Metafunction; + + // The result of at_c + template struct kth_element_const_reference_type; + where Metafunction; + + template kth_element_const_reference_type::type at_c(T); + + template where { ColorBasesCompatibleConcept } + T::T(T2); + template where { ColorBasesCompatibleConcept } + bool operator==(const T&, const T2&); + template where { ColorBasesCompatibleConcept } + bool operator!=(const T&, const T2&); + +}; + +concept MutableColorBaseConcept + : Assignable, Swappable +{ + template struct kth_element_reference_type; + where Metafunction; + + template kth_element_reference_type::type at_c(T); + + template where { ColorBasesCompatibleConcept } + T& operator=(T&, const T2&); +}; + +concept ColorBaseValueConcept : MutableColorBaseConcept, Regular +{ +}; + +concept HomogeneousColorBaseConcept +{ + // For all K in [0 ... size::value-1): + // where SameType::type, kth_element_type::type>; + kth_element_const_reference_type<0>::type dynamic_at_c(const CB&, std::size_t n) const; +}; + +concept MutableHomogeneousColorBaseConcept + : HomogeneousColorBaseConcept +{ + kth_element_reference_type<0>::type dynamic_at_c(const CB&, std::size_t n); +}; + +concept HomogeneousColorBaseValueConcept + : MutableHomogeneousColorBaseConcept, Regular +{ +}; + +concept ColorBasesCompatibleConcept +{ + where SameType; + // also, for all K in [0 ... size::value): + // where Convertible::type, kth_semantic_element_type::type>; + // where Convertible::type, kth_semantic_element_type::type>; +}; +---- + +A color base must have an associated layout (which consists of a color space, +as well as an ordering of the channels). There are two ways to index the +elements of a color base: A physical index corresponds to the way they are +ordered in memory, and a semantic index corresponds to the way the elements +are ordered in their color space. For example, in the RGB color space the +elements are ordered as `{red_t, green_t, blue_t}`. For a color base with +a BGR layout, the first element in physical ordering is the blue element, +whereas the first semantic element is the red one. Models of +`ColorBaseConcept` are required to provide the `at_c(ColorBase)` +function, which allows for accessing the elements based on their physical +order. GIL provides a `semantic_at_c(ColorBase)` function (described +later) which can operate on any model of ColorBaseConcept and returns the +corresponding semantic element. + +Two color bases are _compatible_ if they have the same color space and their +elements (paired semantically) are convertible to each other. + +== Models + +GIL provides a model for a homogeneous color base (a color base whose elements +all have the same type). + +[source,cpp] +---- +namespace detail +{ + template + struct homogeneous_color_base; +} +---- + +It is used in the implementation of GIL's pixel, planar pixel reference and +planar pixel iterator. Another model of `ColorBaseConcept` is +`packed_pixel` - it is a pixel whose channels are bit ranges. + +See the xref:design/pixel.adoc[pixel] section for more. + +== Algorithms + +GIL provides the following functions and metafunctions operating on color +bases: + +[source,cpp] +---- +// 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) +template struct kth_semantic_element_reference_type; +template struct kth_semantic_element_const_reference_type; + +// Returns a reference to the element with K-th semantic index. +template +typename kth_semantic_element_reference_type::type semantic_at_c(ColorBase& p) +template +typename kth_semantic_element_const_reference_type::type semantic_at_c(const ColorBase& p) + +// Returns the type of the return value of get_color(color_base) +template struct color_reference_t; +template struct color_const_reference_t; + +// Returns a reference to the element corresponding to the given color +template +typename color_reference_t::type get_color(ColorBase& cb, Color=Color()); +template +typename color_const_reference_t::type get_color(const ColorBase& cb, Color=Color()); + +// Returns the element type of the color base. Defined for homogeneous color bases only +template struct element_type; +template struct element_reference_type; +template struct element_const_reference_type; +---- + +GIL also provides the following algorithms which operate on color bases. +Note that they all pair the elements semantically: + +[source,cpp] +---- +// Equivalents to std::equal, std::copy, std::fill, std::generate +template bool static_equal(const CB1& p1, const CB2& p2); +template void static_copy(const Src& src, Dst& dst); +template void static_generate(CB& dst,Op op); + +// Equivalents to std::transform +template Op static_transform( CB&,Dst&,Op); +template Op static_transform(const CB&,Dst&,Op); +template Op static_transform( CB1&, CB2&,Dst&,Op); +template Op static_transform(const CB1&, CB2&,Dst&,Op); +template Op static_transform( CB1&,const CB2&,Dst&,Op); +template Op static_transform(const CB1&,const CB2&,Dst&,Op); + +// Equivalents to std::for_each +template Op static_for_each( CB1&,Op); +template Op static_for_each(const CB1&,Op); +template Op static_for_each( CB1&, CB2&,Op); +template Op static_for_each( CB1&,const CB2&,Op); +template Op static_for_each(const CB1&, CB2&,Op); +template Op static_for_each(const CB1&,const CB2&,Op); +template Op static_for_each( CB1&, CB2&, CB3&,Op); +template Op static_for_each( CB1&, CB2&,const CB3&,Op); +template Op static_for_each( CB1&,const CB2&, CB3&,Op); +template Op static_for_each( CB1&,const CB2&,const CB3&,Op); +template Op static_for_each(const CB1&, CB2&, CB3&,Op); +template Op static_for_each(const CB1&, CB2&,const CB3&,Op); +template Op static_for_each(const CB1&,const CB2&, CB3&,Op); +template Op static_for_each(const CB1&,const CB2&,const CB3&,Op); + +// The following algorithms are only defined for homogeneous color bases: +// Equivalent to std::fill +template void static_fill(HCB& p, const Element& v); + +// Equivalents to std::min_element and std::max_element +template typename element_const_reference_type::type static_min(const HCB&); +template typename element_reference_type::type static_min( HCB&); +template typename element_const_reference_type::type static_max(const HCB&); +template typename element_reference_type::type static_max( HCB&); +---- + +These algorithms are designed after the corresponding STL algorithms, except +that instead of ranges they take color bases and operate on their elements. +In addition, they are implemented with a compile-time recursion (thus the +prefix "static_"). Finally, they pair the elements semantically instead of +based on their physical order in memory. + +For example, here is the implementation of `static_equal`: + +[source,cpp] +---- +namespace detail +{ + template struct element_recursion + { + template + static bool static_equal(const P1& p1, const P2& p2) + { + return element_recursion::static_equal(p1,p2) && + semantic_at_c(p1)==semantic_at_c(p2); + } + }; + template <> struct element_recursion<0> + { + template + static bool static_equal(const P1&, const P2&) { return true; } + }; +} + +template +bool static_equal(const P1& p1, const P2& p2) +{ + gil_function_requires >(); + return detail::element_recursion::value>::static_equal(p1,p2); +} +---- + +This algorithm is used when invoking `operator==` on two pixels, for +example. By using semantic accessors we are properly comparing an RGB pixel to +a BGR pixel. Notice also that all of the above algorithms taking more than one +color base require that they all have the same color space. diff --git a/doc/modules/ROOT/pages/design/color_space.adoc b/doc/modules/ROOT/pages/design/color_space.adoc new file mode 100644 index 0000000000..59ec3185c2 --- /dev/null +++ b/doc/modules/ROOT/pages/design/color_space.adoc @@ -0,0 +1,88 @@ += Color Space and Layout + +== Overview + +A color space captures the set and interpretation of channels comprising a +pixel. In Boost.GIL, color space is defined as an MPL random access sequence +containing the types of all elements in the color space. + +Two color spaces are considered _compatible_ if they are equal (i.e. have the +same set of colors in the same order). + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_color_space_concept.html[ColorSpaceConcept] +* link:../reference/structboost_1_1gil_1_1_color_spaces_compatible_concept.html[ColorSpacesCompatibleConcept] +* link:../reference/structboost_1_1gil_1_1_channel_mapping_concept.html[ChannelMappingConcept] +==== + +== Models + +GIL currently provides the following color spaces: + +- `gray_t` +- `rgb_t` +- `rgba_t` +- `cmyk_t` + +It also provides unnamed N-channel color spaces of two to five channels: + +- `devicen_t<2>` +- `devicen_t<3>` +- `devicen_t<4>` +- `devicen_t<5>` + +Besides the standard layouts, it also provides: + +- `bgr_layout_t` +- `bgra_layout_t` +- `abgr_layout_t` +- `argb_layout_t` + +As an example, here is how GIL defines the RGBA color space: + +[source,cpp] +---- +struct red_t {}; +struct green_t {}; +struct blue_t {}; +struct alpha_t {}; +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 +`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. + +The mapping of channels is specified by `ChannelMappingConcept`, which is +an MPL random access sequence of integral types. +A color space and its associated mapping are often used together. + +Thus they are grouped in GIL's layout: + +[source,cpp] +---- +template +< + typename ColorSpace, + typename ChannelMapping = mp11::make_integer_sequence::value> +> +struct layout +{ + using color_space_t = ColorSpace; + using channel_mapping_t = ChannelMapping; +}; +---- + +Here is how to create layouts for the RGBA color space: + +[source,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>; +---- diff --git a/doc/modules/ROOT/pages/design/concepts.adoc b/doc/modules/ROOT/pages/design/concepts.adoc new file mode 100644 index 0000000000..56b7903058 --- /dev/null +++ b/doc/modules/ROOT/pages/design/concepts.adoc @@ -0,0 +1,71 @@ += Concepts + +All constructs in GIL are models of GIL concepts. A _concept_ is a set of +requirements that a type (or a set of related types) must fulfill to be used +correctly in generic algorithms. The requirements include syntactic and +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 at +https://web.archive.org/web/20160324115943/http://www.generic-programming.org/languages/conceptcpp/[Generic Programming in ConceptC++] + +In this document we will use a syntax for defining concepts that is described +in the C++ standard proposal paper +http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2081.pdf[[N2081\] Concepts]. + +Here are some common concepts that will be used in GIL. +Most of them are defined at the +https://web.archive.org/web/20160326060858/http://www.generic-programming.org/languages/conceptcpp/concept_web.php[ConceptC++ Concept Web]: + +[source,cpp] +---- +auto concept DefaultConstructible +{ + T::T(); +}; + +auto concept CopyConstructible +{ + T::T(T); + T::~T(); +}; + +auto concept Assignable +{ + typename result_type; + result_type operator=(T&, U); +}; + +auto concept EqualityComparable +{ + bool operator==(T x, U y); + bool operator!=(T x, U y) { return !(x==y); } +}; + +concept SameType { /* unspecified */ }; +template concept_map SameType { /* unspecified */ }; + +auto concept Swappable +{ + void swap(T& t, T& u); +}; +---- + +Here are some additional basic concepts that GIL needs: + +[source,cpp] +---- +auto concept Regular : + DefaultConstructible, + CopyConstructible, + EqualityComparable, + Assignable, + Swappable +{}; + +auto concept Metafunction +{ + typename type; +}; +---- diff --git a/doc/design/conclusions.rst b/doc/modules/ROOT/pages/design/conclusions.adoc similarity index 84% rename from doc/design/conclusions.rst rename to doc/modules/ROOT/pages/design/conclusions.adoc index 98214f0491..a15dfe1ed6 100644 --- a/doc/design/conclusions.rst +++ b/doc/modules/ROOT/pages/design/conclusions.adoc @@ -1,41 +1,31 @@ -Conclusions -=========== - -.. contents:: - :local: - :depth: 2 += Conclusions The Generic Image Library is designed with the following five goals in mind: -Generality ----------- +== Generality Abstracts image representations from algorithms on images. It allows for writing code once and have it work for any image type. -Performance ------------ +== Performance Speed has been instrumental to the design of the library. The generic algorithms provided in the library are in many cases comparable in speed to hand-coding the algorithm for a specific image type. -Flexibility ------------ +== Flexibility Compile-type parameter resolution results in faster code, but severely limits code flexibility. The library allows for any image parameter to be specified at run time, at a minor performance cost. -Extensibility -------------- +== Extensibility Virtually every construct in GIL can be extended - new channel types, color spaces, layouts, iterators, locators, image views and images can be provided by modeling the corresponding GIL concepts. -Compatibility -------------- +== Compatibility The library is designed as an STL complement. Generic STL algorithms can be used for pixel manipulation, and they are diff --git a/doc/modules/ROOT/pages/design/dynamic_image.adoc b/doc/modules/ROOT/pages/design/dynamic_image.adoc new file mode 100644 index 0000000000..620ead5f06 --- /dev/null +++ b/doc/modules/ROOT/pages/design/dynamic_image.adoc @@ -0,0 +1,215 @@ += Dynamic images and image views + +The GIL extension called `dynamic_image` allows for images, image views +or any GIL constructs to have their parameters defined at run time. + +The color space, channel depth, channel ordering, and interleaved/planar +structure of an image are defined by the type of its template argument, which +makes them compile-time bound. Often some of these parameters are available +only at run time. Consider, for example, writing a module that opens the image +at a given file path, rotates it and saves it back in its original color space +and channel depth. How can we possibly write this using our generic image? +What type is the image loading code supposed to return? + +Here is an example: + +[source,cpp] +---- +#include +using namespace boost; + +#define ASSERT_SAME(A,B) static_assert(is_same< A,B >::value, "") + +// Create any_image class (or any_image_view) class with a set of allowed images +typedef any_image my_any_image_t; + +// Associated view types are available (equivalent to the ones in image_t) +typedef any_image_view AV; +ASSERT_SAME(my_any_image_t::view_t, AV); + +typedef any_image_view> CAV; +ASSERT_SAME(my_any_image_t::const_view_t, CAV); +ASSERT_SAME(my_any_image_t::const_view_t, my_any_image_t::view_t::const_t); + +typedef any_image_view SAV; +ASSERT_SAME(typename dynamic_x_step_type::type, SAV); + +// Assign it a concrete image at run time: +my_any_image_t myImg = my_any_image_t(rgb8_image_t(100,100)); + +// Change it to another at run time. The previous image gets destroyed +myImg = cmyk16_planar_image_t(200,100); + +// Assigning to an image not in the allowed set throws an exception +myImg = gray8_image_t(); // will throw std::bad_cast +---- + +The `any_image` and `any_image_view` subclass from Boost.Variant2 `variant` class, +a never valueless variant type, compatible with `std::variant` in C++17. + +GIL `any_image_view` and `any_image` are subclasses of `variant`: + +[source,cpp] +---- +template +class any_image_view : public variant +{ +public: + typedef ... const_t; // immutable equivalent of this + typedef std::ptrdiff_t x_coord_t; + typedef std::ptrdiff_t y_coord_t; + typedef point point_t; + using size_type = std::size_t; + + any_image_view(); + template explicit any_image_view(const T& obj); + any_image_view(const any_image_view& v); + + template any_image_view& operator=(const T& obj); + any_image_view& operator=(const any_image_view& v); + + // parameters of the currently instantiated view + std::size_t num_channels() const; + point_t dimensions() const; + size_type size() const; + x_coord_t width() const; + y_coord_t height() const; +}; + +template +class any_image : public variant +{ +public: + typedef ... const_view_t; + typedef ... view_t; + typedef std::ptrdiff_t x_coord_t; + typedef std::ptrdiff_t y_coord_t; + typedef point point_t; + + any_image(); + template explicit any_image(const T& obj); + template explicit any_image(T& obj, bool do_swap); + any_image(const any_image& v); + + template any_image& operator=(const T& obj); + any_image& operator=(const any_image& v); + + void recreate(const point_t& dims, unsigned alignment=1); + void recreate(x_coord_t width, y_coord_t height, unsigned alignment=1); + + std::size_t num_channels() const; + point_t dimensions() const; + x_coord_t width() const; + y_coord_t height() const; +}; +---- + +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 +typically have time complexity at least linear on the number of +pixels, the single switch statement of image view variant adds +practically no measurable performance overhead compared to templated +image views. + +Variants behave like the underlying type. Their copy constructor will +invoke the copy constructor of the underlying instance. Equality +operator will check if the two instances are of the same type and then +invoke their `operator==`, etc. The default constructor of a variant +will default-construct the first type. That means that +`any_image_view` has shallow default-constructor, copy-constructor, +assignment and equality comparison, whereas `any_image` has deep +ones. + +It is important to note that even though `any_image_view` and +`any_image` resemble the static `image_view` and `image`, they +do not model the full requirements of `ImageViewConcept` and +`ImageConcept`. In particular they don't provide access to the +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 `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 +`copy_pixels`: + +[source,cpp] +---- +rgb8_view_t v1(...); // concrete image view +bgr8_view_t v2(...); // concrete image view compatible with v1 and of the same size +any_image_view av(...); // run-time specified image view + +// Copies the pixels from v1 into v2. +// If the pixels are incompatible triggers compile error +copy_pixels(v1,v2); + +// The source or destination (or both) may be run-time instantiated. +// If they happen to be incompatible, throws std::bad_cast +copy_pixels(v1, av); +copy_pixels(av, v2); +copy_pixels(av, av); +---- + +By having algorithm overloads supporting dynamic constructs, we create +a base upon which it is possible to write algorithms that can work +with either compile-time or runtime images or views. The following +code, for example, uses the GIL I/O extension to turn an image on disk +upside down: + +[source,cpp] +---- +#include + +template // Could be rgb8_image_t or any_image<...> +void save_180rot(const std::string& file_name) +{ + Image 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 +because all functions it uses have overloads taking runtime +constructs. For example, here is how `rotated180_view` is +implemented: + +[source,cpp] +---- +// implementation using templated view +template +typename dynamic_xy_step_type::type rotated180_view(const View& src) { ... } + +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)); + } + }; +} + +// overloading of the function using variant. Takes and returns run-time bound view. +// The returned view has a dynamic step +template inline +typename dynamic_xy_step_type>::type rotated180_view(const any_image_view& src) +{ + 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 +more than one variant) because they instantiate the algorithm for +every possible model that the variant can take. This can take a toll +on compile time and executable size. Despite these limitations, +`variant` is a powerful technique that allows us to combine the +speed of compile-time resolution with the flexibility of run-time +resolution. It allows us to treat images of different parameters +uniformly as a collection and store them in the same container. diff --git a/doc/modules/ROOT/pages/design/examples.adoc b/doc/modules/ROOT/pages/design/examples.adoc new file mode 100644 index 0000000000..610bb2df2a --- /dev/null +++ b/doc/modules/ROOT/pages/design/examples.adoc @@ -0,0 +1,209 @@ += Examples + +== Pixel-level Operations + +Here are some operations you can do with pixel values, pixel pointers and +pixel references: + +[source,cpp] +---- +rgb8_pixel_t p1(255,0,0); // make a red RGB pixel +bgr8_pixel_t p2 = p1; // RGB and BGR are compatible and the channels will be properly mapped. +assert(p1==p2); // p2 will also be red. +assert(p2[0]!=p1[0]); // operator[] gives physical channel order (as laid down in memory) +assert(semantic_at_c<0>(p1)==semantic_at_c<0>(p2)); // this is how to compare the two red channels +get_color(p1,green_t()) = get_color(p2,blue_t()); // channels can also be accessed by name + +const unsigned char* r; +const unsigned char* g; +const unsigned char* b; +rgb8c_planar_ptr_t ptr(r,g,b); // constructing const planar pointer from const pointers to each plane + +rgb8c_planar_ref_t ref=*ptr; // just like built-in reference, dereferencing a planar pointer returns a planar reference + +p2=ref; p2=p1; p2=ptr[7]; p2=rgb8_pixel_t(1,2,3); // planar/interleaved references and values to RGB/BGR can be freely mixed + +//rgb8_planar_ref_t ref2; // compile error: References have no default constructors +//ref2=*ptr; // compile error: Cannot construct non-const reference by dereferencing const pointer +//ptr[3]=p1; // compile error: Cannot set the fourth pixel through a const pointer +//p1 = pixel();// compile error: Incompatible channel depth +//p1 = pixel();// compile error: Incompatible color space (even though it has the same number of channels) +//p1 = pixel();// compile error: Incompatible color space (even though it contains red, green and blue channels) +---- + +Here is how to use pixels in generic code: + +[source,cpp] +---- +template +void gray_to_rgb(const GrayPixel& src, RGBPixel& dst) +{ + gil_function_requires >(); + gil_function_requires >(); + + typedef typename color_space_type::type gray_cs_t; + static_assert(boost::is_same::value, ""); + + typedef typename color_space_type::type rgb_cs_t; + static_assert(boost::is_same::value, ""); + + typedef typename channel_type::type gray_channel_t; + typedef typename channel_type::type rgb_channel_t; + + gray_channel_t gray = get_color(src,gray_color_t()); + static_fill(dst, channel_convert(gray)); +} + +// example use patterns: + +// converting gray l-value to RGB and storing at (5,5) in a 16-bit BGR interleaved image: +bgr16_view_t b16(...); +gray_to_rgb(gray8_pixel_t(33), b16(5,5)); + +// storing the first pixel of an 8-bit grayscale image as the 5-th pixel of 32-bit planar RGB image: +rgb32f_planar_view_t rpv32; +gray8_view_t gv8(...); +gray_to_rgb(*gv8.begin(), rpv32[5]); +---- + +As the example shows, both the source and the destination can be references or +values, planar or interleaved, as long as they model `PixelConcept` and +`MutablePixelConcept` respectively. + +== Resizing image canvas + +Resizing an image canvas means adding a buffer of pixels around existing +pixels. Size of canvas of an image can never be smaller than the image itself. + +Suppose we want to convolve an image with multiple kernels, the largest of +which is 2K+1 x 2K+1 pixels. It may be worth creating a margin of K pixels +around the image borders. Here is how to do it: + +[source,cpp] +---- +template // Models ImageConcept (the returned image) +void create_with_margin(const SrcView& src, int k, DstImage& result) +{ + gil_function_requires >(); + gil_function_requires >(); + gil_function_requires >(); + + result=DstImage(src.width()+2*k, src.height()+2*k); + typename DstImage::view_t centerImg=subimage_view(view(result), k,k,src.width(),src.height()); + std::copy(src.begin(), src.end(), centerImg.begin()); +} +---- + +We allocated a larger image, then we used `subimage_view` to create a +shallow image of its center area of top left corner at (k,k) and of identical +size as `src`, and finally we copied `src` into that center image. If the +margin needs initialization, we could have done it with `fill_pixels`. Here +is how to simplify this code using the `copy_pixels` algorithm: + +[source,cpp] +---- +template +void create_with_margin(const SrcView& src, int k, DstImage& result) +{ + result.recreate(src.width()+2*k, src.height()+2*k); + copy_pixels(src, subimage_view(view(result), k,k,src.width(),src.height())); +} +---- + +(Note also that `image::recreate` is more efficient than `operator=`, as +the latter will do an unnecessary copy construction.) Not only does the above +example work for planar and interleaved images of any color space and pixel +depth; it is also optimized. GIL overrides `std::copy` - when called on two +identical interleaved images with no padding at the end of rows, it simply +does a `memmove`. For planar images it does `memmove` for each channel. +If one of the images has padding, (as in our case) it will try to do +`memmove` for each row. When an image has no padding, it will use its +lightweight horizontal iterator (as opposed to the more complex 1D image +iterator that has to check for the end of rows). It chooses the fastest method, +taking into account both static and run-time parameters. + +== Histogram + +The histogram can be computed by counting the number of pixel values that fall +in each bin. The following method takes a grayscale (one-dimensional) image +view, since only grayscale pixels are convertible to integers: + +[source,cpp] +---- +template +void grayimage_histogram(const GrayView& img, R& hist) +{ + for (typename GrayView::iterator it=img.begin(); it!=img.end(); ++it) + ++hist[*it]; +} +---- + +Using `boost::lambda` and GIL's `for_each_pixel` algorithm, we can write +this more compactly: + +[source,cpp] +---- +template +void grayimage_histogram(const GrayView& v, R& hist) +{ + for_each_pixel(v, ++var(hist)[_1]); +} +---- + +Where `for_each_pixel` invokes `std::for_each` and `var` and `_1` are +`boost::lambda` constructs. To compute the luminosity histogram, we call the +above method using the grayscale view of an image: + +[source,cpp] +---- +template +void luminosity_histogram(const View& v, R& hist) +{ + grayimage_histogram(color_converted_view(v),hist); +} +---- + +This is how to invoke it: + +[source,cpp] +---- +unsigned char hist[256]; +std::fill(hist,hist+256,0); +luminosity_histogram(my_view,hist); +---- + +If we want to view the histogram of the second channel of the image in the top +left 100x100 area, we call: + +[source,cpp] +---- +grayimage_histogram(nth_channel_view(subimage_view(img,0,0,100,100),1),hist); +---- + +No pixels are copied and no extra memory is allocated - the code operates +directly on the source pixels, which could be in any supported color space and +channel depth. They could be either planar or interleaved. + +== Using image views + +The following code illustrates the power of using image views: + +[source,cpp] +---- +jpeg_read_image("monkey.jpg", img); +step1=view(img); +step2=subimage_view(step1, 200,300, 150,150); +step3=color_converted_view(step2); +step4=rotated180_view(step3); +step5=subsampled_view(step4, 2,1); +jpeg_write_view("monkey_transform.jpg", step5); +---- + +The intermediate images are shown here: + +image::monkey_steps.jpg[] + +Notice that no pixels are ever copied. All the work is done inside +`jpeg_write_view`. If we call our `luminosity_histogram` with +`step5` it will do the right thing. diff --git a/doc/modules/ROOT/pages/design/extending.adoc b/doc/modules/ROOT/pages/design/extending.adoc new file mode 100644 index 0000000000..faec5c2726 --- /dev/null +++ b/doc/modules/ROOT/pages/design/extending.adoc @@ -0,0 +1,162 @@ += Extending + +== Overview + +You can define your own pixel iterators, locators, image views, +images, channel types, color spaces and algorithms. You can make +virtual images that live on the disk, inside a jpeg file, somewhere on +the internet, or even fully-synthetic images such as the Mandelbrot +set. As long as they properly model the corresponding concepts, they +will work with any existing GIL code. Most such extensions require no +changes to the library and can thus be supplied in another module. + +== Defining new color spaces + +Each color space is in a separate file. To add a new color space, just +copy one of the existing ones (like rgb.hpp) and change it +accordingly. If you want color conversion support, you will have to +provide methods to convert between it and the existing color spaces +(see color_convert.h). For convenience you may want to provide useful +typedefs for pixels, pointers, references and images with the new +color space (see typedefs.h). + +== Defining new channel types + +Most of the time you don't need to do anything special to use a new +channel type. You can just use it: + +[source,cpp] +---- +typedef pixel rgb64_pixel_t; // 64 bit RGB pixel +typedef rgb64_pixel* rgb64_pixel_ptr_t;// pointer to 64-bit interleaved data +typedef image_type::type rgb64_image_t; // 64-bit interleaved image +---- + +If you want to use your own channel class, you will need to provide a +specialization of `channel_traits` for it (see channel.hpp). If you +want to do conversion between your and existing channel types, you +will need to provide an overload of `channel_convert`. + +== Overloading color conversion + +Suppose you want to provide your own color conversion. For example, +you may want to implement higher quality color conversion using color +profiles. Typically you may want to redefine color conversion only in +some instances and default to GIL's color conversion in all other +cases. Here is, for example, how to overload color conversion so that +color conversion to gray inverts the result but everything else +remains the same: + +[source,cpp] +---- +// make the default use GIL's default +template +struct my_color_converter_impl +: public default_color_converter_impl {}; + +// provide specializations only for cases you care about +// (in this case, if the destination is grayscale, invert it) +template +struct my_color_converter_impl +{ + template // Model PixelConcept + void operator()(const SrcP& src, DstP& dst) const + { + default_color_converter_impl()(src,dst); + get_color(dst,gray_color_t())=channel_invert(get_color(dst,gray_color_t())); + } +}; + +// create a color converter object that dispatches to your own implementation +struct my_color_converter +{ + template // Model PixelConcept + void operator()(const SrcP& src,DstP& dst) const + { + typedef typename color_space_type::type SrcColorSpace; + typedef typename color_space_type::type DstColorSpace; + my_color_converter_impl()(src,dst); + } +}; +---- + +GIL color conversion functions take the color converter as an +optional parameter. You can pass your own color converter: + +[source,cpp] +---- +color_converted_view(img_view,my_color_converter()); +---- + +== Defining new image views + +You can provide your own pixel iterators, locators and views, +overriding either the mechanism for getting from one pixel to the next +or doing an arbitrary pixel transformation on dereference. For +example, let's look at the implementation of `color_converted_view` +(an image factory method that, given any image view, returns a new, +otherwise identical view, except that color conversion is performed on +pixel access). First we need to define a model of +`PixelDereferenceAdaptorConcept`; a function object that will be +called when we dereference a pixel iterator. It will call +`color_convert` to convert to the destination pixel type: + +[source,cpp] +---- +template // Destination pixel value (models PixelValueConcept) +class color_convert_deref_fn +{ +public: + typedef color_convert_deref_fn const_t; + typedef DstP value_type; + typedef value_type reference; // read-only dereferencing + typedef const value_type& const_reference; + typedef SrcConstRefP argument_type; + typedef reference result_type; + static bool constexpr is_mutable = false; + + result_type operator()(argument_type srcP) const { + result_type dstP; + color_convert(srcP,dstP); + return dstP; + } +}; +---- + +We then use the `add_deref` member struct of image views to construct the +type of a view that invokes a given function object (`deref_t`) upon +dereferencing. In our case, it performs color conversion: + +[source,cpp] +---- +template +struct color_converted_view_type +{ +private: + typedef typename SrcView::const_t::reference src_pix_ref; // const reference to pixel in SrcView + typedef color_convert_deref_fn deref_t; // the dereference adaptor that performs color conversion + typedef typename SrcView::template add_deref add_ref_t; +public: + typedef typename add_ref_t::type type; // the color converted view type + static type make(const SrcView& sv) { return add_ref_t::make(sv, deref_t()); } +}; +---- + +Finally our `color_converted_view` code simply creates color-converted view +from the source view: + +[source,cpp] +---- +template inline +typename color_converted_view_type::type color_convert_view(const View& src) +{ + return color_converted_view_type::make(src); +} +---- + +(The actual color convert view transformation is slightly more +complicated, as it takes an optional color conversion object, which +allows users to specify their own color conversion methods.) See the +GIL tutorial for an example of creating a virtual image view that +defines the Mandelbrot set. diff --git a/doc/modules/ROOT/pages/design/image.adoc b/doc/modules/ROOT/pages/design/image.adoc new file mode 100644 index 0000000000..3d4c77e2ed --- /dev/null +++ b/doc/modules/ROOT/pages/design/image.adoc @@ -0,0 +1,103 @@ += Image + +== Overview + +An image is a container that owns the pixels of a given image view. +It allocates them in its constructor and deletes them in the destructor. +It has a deep assignment operator and copy constructor. Images are used +rarely, just when data ownership is important. Most STL algorithms operate on +ranges, not containers. Similarly most GIL algorithms operate on image views +(which images provide). + +In the most general form images are N-dimensional and satisfy the following +concept: + +[source,cpp] +---- +concept RandomAccessNDImageConcept : Regular +{ + typename view_t; where MutableRandomAccessNDImageViewConcept; + typename const_view_t = view_t::const_t; + typename point_t = view_t::point_t; + typename value_type = view_t::value_type; + typename allocator_type; + + Img::Img(point_t dims, std::size_t alignment=0); + Img::Img(point_t dims, value_type fill_value, std::size_t alignment); + + void Img::recreate(point_t new_dims, std::size_t alignment=0); + void Img::recreate(point_t new_dims, value_type fill_value, std::size_t alignment); + + const point_t& Img::dimensions() const; + const const_view_t& const_view(const Img&); + const view_t& view(Img&); +}; +---- + +Two-dimensional images have additional requirements: + +[source,cpp] +---- +concept RandomAccess2DImageConcept +{ + typename x_coord_t = const_view_t::x_coord_t; + typename y_coord_t = const_view_t::y_coord_t; + + Img::Img(x_coord_t width, y_coord_t height, std::size_t alignment=0); + Img::Img(x_coord_t width, y_coord_t height, value_type fill_value, std::size_t alignment); + + x_coord_t Img::width() const; + y_coord_t Img::height() const; + + void Img::recreate(x_coord_t width, y_coord_t height, std::size_t alignment=1); + void Img::recreate(x_coord_t width, y_coord_t height, value_type fill_value, std::size_t alignment); +}; +---- + +GIL images have views that model `ImageViewConcept` and operate on pixels. + +[source,cpp] +---- +concept ImageConcept +{ + where MutableImageViewConcept; + typename coord_t = view_t::coord_t; +}; +---- + +Images, unlike locators and image views, don't have 'mutable' set of concepts +because immutable images are not very useful. + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_random_access_n_d_image_concept.html[RandomAccessNDImageConcept] +* link:../reference/structboost_1_1gil_1_1_random_access2_d_image_concept.html[RandomAccess2DImageConcept] +* link:../reference/structboost_1_1gil_1_1_image_concept.html[ImageConcept] +==== + +== Models + +GIL provides a class, `image`, which is templated over the value type +(the pixel) and models `ImageConcept`: + +[source,cpp] +---- + template + < + typename Pixel, // Models PixelValueConcept + bool IsPlanar, // planar or interleaved image + typename A=std::allocator + > + class image; +---- + +The image constructor takes an alignment parameter which allows for +constructing images that are word-aligned or 8-byte aligned. The alignment is +specified in bytes. The default value for alignment is 0, which means there is +no padding at the end of rows. Many operations are faster using such +1D-traversable images, because `image_view::x_iterator` can be used to +traverse the pixels, instead of the more complicated `image_view::iterator`. +Note that when alignment is 0, packed images are aligned to the bit - i.e. +there are no padding bits at the end of rows of packed images. diff --git a/doc/modules/ROOT/pages/design/image_view.adoc b/doc/modules/ROOT/pages/design/image_view.adoc new file mode 100644 index 0000000000..bc0ab1ea00 --- /dev/null +++ b/doc/modules/ROOT/pages/design/image_view.adoc @@ -0,0 +1,437 @@ += Image View + +== Overview + +An image view is a generalization of STL range concept to multiple dimensions. +Similar to ranges (and iterators), image views are shallow, don't own the +underlying data and don't propagate their constness over the data. +For example, a constant image view cannot be resized, but may allow modifying +the pixels. For pixel-immutable operations, use constant-value image view +(also called non-mutable image view). Most general N-dimensional views satisfy +the following concept: + +[source,cpp] +---- +concept RandomAccessNDImageViewConcept +{ + typename value_type; // for pixel-based views, the pixel type + typename reference; // result of dereferencing + typename difference_type; // result of operator-(iterator,iterator) (1-dimensional!) + typename const_t; where RandomAccessNDImageViewConcept; // same as View, but over immutable values + typename point_t; where PointNDConcept; // N-dimensional point + typename locator; where RandomAccessNDLocatorConcept; // N-dimensional locator. + typename iterator; where RandomAccessTraversalConcept; // 1-dimensional iterator over all values + typename reverse_iterator; where RandomAccessTraversalConcept; + typename size_type; // the return value of size() + + // Equivalent to RandomAccessNDLocatorConcept::axis + template struct axis { + typename coord_t = point_t::axis::coord_t; + typename iterator; where RandomAccessTraversalConcept; // iterator along D-th axis. + where SameType; + where SameType; + }; + + // Defines the type of a view similar to this type, except it invokes Deref upon dereferencing + template struct add_deref { + typename type; where RandomAccessNDImageViewConcept; + static type make(const View& v, const Deref& deref); + }; + + static const size_t num_dimensions = point_t::num_dimensions; + + // Create from a locator at the top-left corner and dimensions + View::View(const locator&, const point_type&); + + size_type View::size() const; // total number of elements + reference operator[](View, const difference_type&) const; // 1-dimensional reference + iterator View::begin() const; + iterator View::end() const; + reverse_iterator View::rbegin() const; + reverse_iterator View::rend() const; + iterator View::at(const point_t&); + point_t View::dimensions() const; // number of elements along each dimension + bool View::is_1d_traversable() const; // Does an iterator over the first dimension visit each value? + + // iterator along a given dimension starting at a given point + template View::axis::iterator View::axis_iterator(const point_t&) const; + + reference operator()(View,const point_t&) const; +}; + +concept MutableRandomAccessNDImageViewConcept +{ + where Mutable; +}; +---- + +Two-dimensional image views have the following extra requirements: + +[source,cpp] +---- +concept RandomAccess2DImageViewConcept +{ + where num_dimensions==2; + + typename x_iterator = axis<0>::iterator; + typename y_iterator = axis<1>::iterator; + typename x_coord_t = axis<0>::coord_t; + typename y_coord_t = axis<1>::coord_t; + typename xy_locator = locator; + + x_coord_t View::width() const; + y_coord_t View::height() const; + + // X-navigation + x_iterator View::x_at(const point_t&) 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::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; + + // (x,y) versions of all methods taking point_t + View::View(x_coord_t,y_coord_t,const locator&); + iterator View::at(x_coord_t,y_coord_t) const; + reference operator()(View,x_coord_t,y_coord_t) const; + xy_locator View::xy_at(x_coord_t,y_coord_t) const; + x_iterator View::x_at(x_coord_t,y_coord_t) const; + y_iterator View::y_at(x_coord_t,y_coord_t) const; +}; + +concept MutableRandomAccess2DImageViewConcept + : MutableRandomAccessNDImageViewConcept {}; +---- + +Image views that GIL typically uses operate on value types that model +`PixelValueConcept` and have some additional requirements: + +[source,cpp] +---- +concept ImageViewConcept +{ + where PixelValueConcept; + where PixelIteratorConcept; + where PixelIteratorConcept; + where x_coord_t == y_coord_t; + + typename coord_t = x_coord_t; + + std::size_t View::num_channels() const; +}; + + +concept MutableImageViewConcept + : MutableRandomAccess2DImageViewConcept +{}; +---- + +Two image views are compatible if they have compatible pixels and the same +number of dimensions: + +[source,cpp] +---- +concept ViewsCompatibleConcept +{ + where PixelsCompatibleConcept; + where V1::num_dimensions == V2::num_dimensions; +}; +---- + +Compatible views must also have the same dimensions (i.e. the same width and +height). Many algorithms taking multiple views require that they be pairwise +compatible. + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_random_access_n_d_image_view_concept.html[RandomAccessNDImageViewConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_random_access_n_d_image_view_concept.html[MutableRandomAccessNDImageViewConcept] +* link:../reference/structboost_1_1gil_1_1_random_access2_d_image_view_concept.html[RandomAccess2DImageViewConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_random_access2_d_image_view_concept.html[MutableRandomAccess2DImageViewConcept] +* link:../reference/structboost_1_1gil_1_1_image_view_concept.html[ImageViewConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_image_view_concept.html[MutableImageViewConcept] +* link:../reference/structboost_1_1gil_1_1_views_compatible_concept.html[ViewsCompatibleConcept] +==== + +== Models + +GIL provides a model for `ImageViewConcept` called `image_view`. It is +templated over a model of `PixelLocatorConcept`. (If instantiated with a +model of `MutablePixelLocatorConcept`, it models +`MutableImageViewConcept`.) Synopsis: + +[source,cpp] +---- +// Locator models PixelLocatorConcept, could be MutablePixelLocatorConcept +template +class image_view +{ +public: + typedef Locator xy_locator; + typedef iterator_from_2d iterator; + ... +private: + xy_locator _pixels; // 2D pixel locator at the top left corner of the image view range + point_t _dimensions; // width and height +}; +---- + +Image views are lightweight objects. A regular interleaved view is typically +16 bytes long - two integers for the width and height (inside dimensions), one +for the number of bytes between adjacent rows (inside the locator) and one +pointer to the beginning of the pixel block. + +== Algorithms + +GIL provides algorithms constructing views from raw data or other views. + +=== Creating Views from Raw Pixels + +Standard image views can be constructed from raw data of any supported color +space, bit depth, channel ordering or planar vs. interleaved structure. +Interleaved views are constructed using `interleaved_view`, supplying the +image dimensions, number of bytes per row, and a pointer to the first pixel: + +[source,cpp] +---- +// Iterator models pixel iterator (e.g. rgb8_ptr_t or rgb8c_ptr_t) +template +image_view<...> interleaved_view(ptrdiff_t width, ptrdiff_t height, Iterator pixels, ptrdiff_t rowsize) +---- + +Planar views are defined for every color space and take each plane separately. +Here is the RGB one: + +[source,cpp] +---- +// Iterator models channel iterator (e.g. bits8* or bits8 const*) +template +image_view<...> planar_rgb_view( + ptrdiff_t width, ptrdiff_t height, + IC r, IC g, IC b, ptrdiff_t rowsize); +---- + +Note that the supplied pixel/channel iterators could be constant (read-only), +in which case the returned view is a constant-value (immutable) view. + +=== Creating Image Views from Other Image Views + +It is possible to construct one image view from another by changing some +policy of how image data is interpreted. The result could be a view whose type +is derived from the type of the source. GIL uses the following metafunctions +to get the derived types: + +[source,cpp] +---- +// Some result view types +template +struct dynamic_xy_step_type : public dynamic_y_step_type::type> {}; + +template +struct dynamic_xy_step_transposed_type : public dynamic_xy_step_type::type> {}; + +// color and bit depth converted view to match pixel type P +template +struct color_converted_view_type +{ + typedef ... type; // image view adaptor with value type DstP, over SrcView +}; + +// single-channel view of the N-th channel of a given view +template +struct nth_channel_view_type +{ + typedef ... type; +}; +---- + +GIL provides the following view transformations: + +[source,cpp] +---- +// flipped upside-down, left-to-right, transposed view +template typename dynamic_y_step_type::type flipped_up_down_view(const View& src); +template typename dynamic_x_step_type::type flipped_left_right_view(const View& src); +template typename dynamic_xy_step_transposed_type::type transposed_view(const View& src); + +// rotations +template typename dynamic_xy_step_type::type rotated180_view(const View& src); +template typename dynamic_xy_step_transposed_type::type rotated90cw_view(const View& src); +template typename dynamic_xy_step_transposed_type::type rotated90ccw_view(const View& src); + +// view of an axis-aligned rectangular area within an image +template View subimage_view(const View& src, + const View::point_t& top_left, const View::point_t& dimensions); + +// subsampled view (skipping pixels in X and Y) +template typename dynamic_xy_step_type::type subsampled_view(const View& src, + const View::point_t& step); + +template +color_converted_view_type::type color_converted_view(const View& src); +template // with a custom color converter +color_converted_view_type::type color_converted_view(const View& src); + +template +nth_channel_view_type::view_t nth_channel_view(const View& view, int n); +---- + +The implementations of most of these view factory methods are straightforward. +Here is, for example, how the flip views are implemented. The flip upside-down +view creates a view whose first pixel is the bottom left pixel of the original +view and whose y-step is the negated step of the source. + +[source,cpp] +---- +template +typename dynamic_y_step_type::type flipped_up_down_view(const View& src) +{ + gil_function_requires >(); + typedef typename dynamic_y_step_type::type RView; + return RView(src.dimensions(),typename RView::xy_locator(src.xy_at(0,src.height()-1),-1)); +} +---- + +The call to `gil_function_requires` ensures (at compile time) that the +template parameter is a valid model of `ImageViewConcept`. Using it +generates easier to track compile errors, creates no extra code and has no +run-time performance impact. We are using the `boost::concept_check library`, +but wrapping it in `gil_function_requires`, which performs the check if the +`BOOST_GIL_USE_CONCEPT_CHECK` is set. It is unset by default, because there +is a significant increase in compile time when using concept checks. We will +skip `gil_function_requires` in the code examples in this guide for the sake +of succinctness. + +Image views can be freely composed (see section xref:design/metafunctions.adoc[metafunctions] for +explanation of the typedefs `rgb16_image_t` and `gray16_step_view_t)`: + +[source,cpp] +---- +rgb16_image_t img(100,100); // an RGB interleaved image + +// grayscale view over the green (index 1) channel of img +gray16_step_view_t green=nth_channel_view(view(img),1); + +// 50x50 view of the green channel of img, upside down and taking every other pixel in X and in Y +gray16_step_view_t ud_fud=flipped_up_down_view(subsampled_view(green,2,2)); +---- + +As previously stated, image views are fast, constant-time, shallow views over +the pixel data. The above code does not copy any pixels; it operates on the +pixel data allocated when `img` was created. + +=== STL-Style Algorithms on Image Views + +Image views provide 1D iteration of their pixels via `begin()` and `end()` +methods, which makes it possible to use STL algorithms with them. However, +using nested loops over X and Y is in many cases more efficient. +The algorithms in this section resemble STL algorithms, but they abstract away +the nested loops and take views (as opposed to ranges) as input. + +[source,cpp] +---- +// Equivalents of std::copy and std::uninitialized_copy +// where ImageViewConcept, MutableImageViewConcept, ViewsCompatibleConcept +template +void copy_pixels(const V1& src, const V2& dst); +template +void uninitialized_copy_pixels(const V1& src, const V2& dst); + +// Equivalents of std::fill and std::uninitialized_fill +// where MutableImageViewConcept, PixelConcept, PixelsCompatibleConcept +template +void fill_pixels(const V& dst, const Value& val); +template +void uninitialized_fill_pixels(const V& dst, const Value& val); + +// Equivalent of std::for_each +// where ImageViewConcept, boost::UnaryFunctionConcept +// where PixelsCompatibleConcept +template +F for_each_pixel(const V& view, F fun); +template +F for_each_pixel_position(const V& view, F fun); + +// Equivalent of std::generate +// where MutableImageViewConcept, boost::UnaryFunctionConcept +// where PixelsCompatibleConcept +template +void generate_pixels(const V& dst, F fun); + +// Equivalent of std::transform with one source +// where ImageViewConcept, MutableImageViewConcept +// where boost::UnaryFunctionConcept +// where PixelsCompatibleConcept +// where PixelsCompatibleConcept +template +F transform_pixels(const V1& src, const V2& dst, F fun); +template +F transform_pixel_positions(const V1& src, const V2& dst, F fun); + +// Equivalent of std::transform with two sources +// where ImageViewConcept, ImageViewConcept, MutableImageViewConcept +// where boost::BinaryFunctionConcept +// where PixelsCompatibleConcept +// where PixelsCompatibleConcept +// where PixelsCompatibleConcept +template +F transform_pixels(const V1& src1, const V2& src2, const V3& dst, F fun); +template +F transform_pixel_positions(const V1& src1, const V2& src2, const V3& dst, F fun); + +// Copies a view into another, color converting the pixels if needed, with the default or user-defined color converter +// where ImageViewConcept, MutableImageViewConcept +// V1::value_type must be convertible to V2::value_type. +template +void copy_and_convert_pixels(const V1& src, const V2& dst); +template +void copy_and_convert_pixels(const V1& src, const V2& dst, ColorConverter ccv); + +// Equivalent of std::equal +// where ImageViewConcept, ImageViewConcept, ViewsCompatibleConcept +template +bool equal_pixels(const V1& view1, const V2& view2); +---- + +Algorithms that take multiple views require that they have the same +dimensions. `for_each_pixel_position` and `transform_pixel_positions` pass +pixel locators, as opposed to pixel references, to their function objects. +This allows for writing algorithms that use pixel neighbours, as the tutorial +demonstrates. + +Most of these algorithms check whether the image views are 1D-traversable. +A 1D-traversable image view has no gaps at the end of the rows. +In other words, if an x_iterator of that view is advanced past the last pixel +in a row it will move to the first pixel of the next row. When image views are +1D-traversable, the algorithms use a single loop and run more efficiently. +If one or more of the input views are not 1D-traversable, the algorithms +fall-back to an X-loop nested inside a Y-loop. + +The algorithms typically delegate the work to their corresponding STL +algorithms. For example, `copy_pixels` calls `std::copy` either for each +row, or, when the images are 1D-traversable, once for all pixels. + +In addition, overloads are sometimes provided for the STL algorithms. +For example, `std::copy` for planar iterators is overloaded to perform +`std::copy` for each of the planes. `std::copy` over bitwise-copyable +pixels results in `std::copy` over unsigned char, which STL +implements via `memmove`. + +As a result `copy_pixels` may result in a single call to `memmove` for +interleaved 1D-traversable views, or one per each plane of planar +1D-traversable views, or one per each row of interleaved non-1D-traversable +images, etc. + +GIL also provides some beta-versions of image processing algorithms, such as +resampling and convolution in a numerics extension available on +http://stlab.adobe.com/gil/download.html. This code is in early stage of +development and is not optimized for speed diff --git a/doc/modules/ROOT/pages/design/index.adoc b/doc/modules/ROOT/pages/design/index.adoc new file mode 100644 index 0000000000..02baf2cb00 --- /dev/null +++ b/doc/modules/ROOT/pages/design/index.adoc @@ -0,0 +1,4 @@ += Design Guide + +The sections listed below are dedicated to describe the design, +structure and basic elements of the Generic Image Library (GIL). diff --git a/doc/modules/ROOT/pages/design/metafunctions.adoc b/doc/modules/ROOT/pages/design/metafunctions.adoc new file mode 100644 index 0000000000..e88fe77f5c --- /dev/null +++ b/doc/modules/ROOT/pages/design/metafunctions.adoc @@ -0,0 +1,244 @@ += Metafunctions + +== Overview + +Flexibility comes at a price. GIL types can be very long and hard to read. +To address this problem, GIL provides typedefs to refer to any standard image, +pixel iterator, pixel locator, pixel reference or pixel value. + +They follow this pattern: + +[source,cpp] +---- +*ColorSpace* + *BitDepth* + ["s|f"] + ["c"] + ["_planar"] + ["_step"] + *ClassType* + "_t" +---- + +where *ColorSpace* also indicates the ordering of components. + +Examples are `rgb`, `bgr`, `cmyk`, `rgba`. *BitDepth* can be, for +example, `8`, `16`, `32`. By default the bits are unsigned integral type. +Append `s` to the bit depth to indicate signed integral, or `f` to +indicate floating point. `c` indicates object whose associated pixel +reference is immutable. `_planar` indicates planar organization (as opposed +to interleaved). `_step` indicates the type has a dynamic step and +*ClassType* is `_image` (image, using a standard allocator), `_view` +(image view), `_loc` (pixel locator), `_ptr` (pixel iterator), `_ref` +(pixel reference), `_pixel` (pixel value). + +Here are examples: + +[source,cpp] +---- +bgr8_image_t i; // 8-bit unsigned (unsigned char) interleaved BGR image +cmyk16_pixel_t; x; // 16-bit unsigned (unsigned short) CMYK pixel value +cmyk16sc_planar_ref_t p(x); // const reference to a 16-bit signed integral (signed short) planar CMYK pixel x +rgb32f_planar_step_ptr_t ii; // step iterator to a floating point 32-bit (float) planar RGB pixel +---- + +== Homogeneous memory-based images + +GIL provides the metafunctions that return the types of standard +homogeneous memory-based GIL constructs given a channel type, a +layout, and whether the construct is planar, has a step along the X +direction, and is mutable: + +[source,cpp] +---- +template +struct pixel_reference_type { typedef ... type; }; + +template +struct pixel_value_type { typedef ... type; }; + +template +struct iterator_type { typedef ... type; }; + +template +struct locator_type { typedef ... type; }; + +template +struct view_type { typedef ... type; }; + +template > +struct image_type { typedef ... type; }; + +template > +struct packed_image_type { typedef ... type; }; + +template > +struct bit_aligned_image_type { typedef ... type; }; +---- + +== Packed and bit-aligned images + +There are also helper metafunctions to construct packed and +bit-aligned images with up to five channels: + +[source,cpp] +---- +template > +struct packed_image1_type { typedef ... type; }; + +template > +struct packed_image2_type { typedef ... type; }; + +template > +struct packed_image3_type { typedef ... type; }; + +template > +struct packed_image4_type { typedef ... type; }; + +template > +struct packed_image5_type { typedef ... type; }; + +template > +struct bit_aligned_image1_type { typedef ... type; }; + +template > +struct bit_aligned_image2_type { typedef ... type; }; + +template > +struct bit_aligned_image3_type { typedef ... type; }; + +template > +struct bit_aligned_image4_type { typedef ... type; }; + +template > +struct bit_aligned_image5_type { typedef ... type; }; +---- + +== Iterators and views + +Here `ChannelValue` models `ChannelValueConcept`. We don't need +`IsYStep` because GIL's memory-based locator and view already allow +the vertical step to be specified dynamically. Iterators and views can +be constructed from a pixel type: + +[source,cpp] +---- +template +struct iterator_type_from_pixel { typedef ... type; }; + +template +struct view_type_from_pixel { typedef ... type; }; +---- + +Using a heterogeneous pixel type will result in heterogeneous iterators and +views. Types can also be constructed from horizontal iterator: + +[source,cpp] +---- +template +struct type_from_x_iterator +{ + typedef ... step_iterator_t; + typedef ... xy_locator_t; + typedef ... view_t; +}; +---- + +== Pixel components + +You can get pixel-related types of any pixel-based GIL constructs (pixels, +iterators, locators and views) using the following metafunctions provided by +`PixelBasedConcept`, `HomogeneousPixelBasedConcept` and metafunctions +built on top of them: + +[source,cpp] +---- +template struct color_space_type { typedef ... type; }; +template struct channel_mapping_type { typedef ... type; }; +template struct is_planar { typedef ... type; }; + +// Defined by homogeneous constructs +template struct channel_type { typedef ... type; }; +template struct num_channels { typedef ... type; }; +---- + +== Deriving and manipulating existing types + +There are metafunctions to construct the type of a construct from an existing +type by changing one or more of its properties: + +[source,cpp] +---- +template +struct derived_pixel_reference_type +{ + typedef ... type; // Models PixelConcept +}; + +template +struct derived_iterator_type +{ + typedef ... type; // Models PixelIteratorConcept +}; + +template +struct derived_view_type +{ + typedef ... type; // Models ImageViewConcept +}; + +template +struct derived_image_type +{ + typedef ... type; // Models ImageConcept +}; +---- + +You can replace one or more of its properties and use `boost::use_default` +for the rest. In this case `IsPlanar`, `IsStep` and `IsMutable` are +MPL boolean constants. For example, here is how to create the type of a view +just like `View`, but being grayscale and planar: + +[source,cpp] +---- +using VT = typename derived_view_type::type; +---- + +== Type traits + +These are metafunctions, some of which return integral types which can be +evaluated like this: + +[source,cpp] +---- +static_assert(is_planar::value == true, ""); +---- + +GIL also supports type analysis metafunctions of the form: + +[source,cpp] +---- +[pixel_reference/iterator/locator/view/image] + "_is_" + [basic/mutable/step] +---- + +For example: + +[source,cpp] +---- +if (view_is_mutable::value) +{ + ... +} +---- + +A _basic_ GIL construct is a memory-based construct that uses the built-in GIL +classes and does not have any function object to invoke upon dereferencing. +For example, a simple planar or interleaved, step or non-step RGB image view +is basic, but a color converted view or a virtual view is not. diff --git a/doc/modules/ROOT/pages/design/pixel.adoc b/doc/modules/ROOT/pages/design/pixel.adoc new file mode 100644 index 0000000000..60067a44c9 --- /dev/null +++ b/doc/modules/ROOT/pages/design/pixel.adoc @@ -0,0 +1,289 @@ += Pixel + +== Overview + +A pixel is a set of channels defining the color at a given point in an +image. Conceptually, a pixel is little more than a color base whose +elements model `ChannelConcept`. All properties of pixels inherit +from color bases: pixels may be _homogeneous_ if all of their channels +have the same type; otherwise they are called _heterogeneous_. The +channels of a pixel may be addressed using semantic or physical +indexing, or by color; all color-base algorithms work on pixels as +well. Two pixels are _compatible_ if their color spaces are the same +and their channels, paired semantically, are compatible. Note that +constness, memory organization and reference/value are ignored. For +example, an 8-bit RGB planar reference is compatible to a constant +8-bit BGR interleaved pixel value. Most pairwise pixel operations +(copy construction, assignment, equality, etc.) are only defined for +compatible pixels. + +Pixels (as well as other GIL constructs built on pixels, such as +iterators, locators, views and images) must provide metafunctions to +access their color space, channel mapping, number of channels, and +(for homogeneous pixels) the channel type: + +[source,cpp] +---- +concept PixelBasedConcept +{ + typename color_space_type; + where Metafunction >; + where ColorSpaceConcept::type>; + typename channel_mapping_type; + where Metafunction >; + where ChannelMappingConcept::type>; + typename is_planar; + where Metafunction >; + where SameType::type, bool>; +}; + +concept HomogeneousPixelBasedConcept +{ + typename channel_type; + where Metafunction >; + where ChannelConcept::type>; +}; +---- + +Pixels model the following concepts: + +[source,cpp] +---- +concept PixelConcept : ColorBaseConcept

, PixelBasedConcept

+{ + where is_pixel

::value==true; + // where for each K [0..size

::value-1]: + // ChannelConcept >; + + typename value_type; where PixelValueConcept; + typename reference; where PixelConcept; + typename const_reference; where PixelConcept; + static const bool P::is_mutable; + + template where { PixelConcept } + P::P(P2); + template where { PixelConcept } + bool operator==(const P&, const P2&); + template where { PixelConcept } + bool operator!=(const P&, const P2&); +}; + +concept MutablePixelConcept : PixelConcept

, MutableColorBaseConcept

+{ + where is_mutable==true; +}; + +concept HomogeneousPixelConcept : HomogeneousColorBaseConcept

, HomogeneousPixelBasedConcept

+{ + P::template element_const_reference_type

::type operator[](P p, std::size_t i) const { return dynamic_at_c(P,i); } +}; + +concept MutableHomogeneousPixelConcept : MutableHomogeneousColorBaseConcept

+{ + P::template element_reference_type

::type operator[](P p, std::size_t i) { return dynamic_at_c(p,i); } +}; + +concept PixelValueConcept : PixelConcept

, Regular

+{ + where SameType; +}; + +concept PixelsCompatibleConcept : ColorBasesCompatibleConcept +{ + // where for each K [0..size::value): + // ChannelsCompatibleConcept::type, kth_semantic_element_type::type>; +}; +---- + +A pixel is _convertible_ to a second pixel if it is possible to +approximate its color in the form of the second pixel. Conversion is +an explicit, non-symmetric and often lossy operation (due to both +channel and color space approximation). Convertibility requires +modeling the following concept: + +[source,cpp] +---- +template +concept PixelConvertibleConcept +{ + void color_convert(const SrcPixel&, DstPixel&); +}; +---- + +The distinction between `PixelConcept` and `PixelValueConcept` is +analogous to that for channels and color bases - pixel reference proxies model +both, but only pixel values model the latter. + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_pixel_based_concept.html[PixelBasedConcept

] +* link:../reference/structboost_1_1gil_1_1_pixel_concept.html[PixelConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_pixel_concept.html[MutablePixelConcept] +* link:../reference/structboost_1_1gil_1_1_pixel_value_concept.html[PixelValueConcept] +* link:../reference/structboost_1_1gil_1_1_homogeneous_pixel_based_concept.html[HomogeneousPixelConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_homogeneous_pixel_concept.html[MutableHomogeneousPixelConcept] +* link:../reference/structboost_1_1gil_1_1_homogeneous_pixel_value_concept.html[HomogeneousPixelValueConcept] +* link:../reference/structboost_1_1gil_1_1_pixels_compatible_concept.html[PixelsCompatibleConcept] +* link:../reference/structboost_1_1gil_1_1_pixel_convertible_concept.html[PixelConvertibleConcept] +==== + +== Models + +The most commonly used pixel is a homogeneous pixel whose values are +together in memory. For this purpose GIL provides the struct +`pixel`, templated over the channel value and layout: + +[source,cpp] +---- +// models HomogeneousPixelValueConcept +template struct pixel; + +// Those typedefs are already provided by GIL +typedef pixel rgb8_pixel_t; +typedef pixel bgr8_pixel_t; + +bgr8_pixel_t bgr8(255,0,0); // pixels can be initialized with the channels directly +rgb8_pixel_t rgb8(bgr8); // compatible pixels can also be copy-constructed + +rgb8 = bgr8; // assignment and equality is defined between compatible pixels +assert(rgb8 == bgr8); // assignment and equality operate on the semantic channels + +// The first physical channels of the two pixels are different +assert(at_c<0>(rgb8) != at_c<0>(bgr8)); +assert(dynamic_at_c(bgr8,0) != dynamic_at_c(rgb8,0)); +assert(rgb8[0] != bgr8[0]); // same as above (but operator[] is defined for pixels only) +---- + +Planar pixels have their channels distributed in memory. While they share the +same value type (`pixel`) with interleaved pixels, their reference type is a +proxy class containing references to each of the channels. +This is implemented with the struct `planar_pixel_reference`: + +[source,cpp] +---- +// models HomogeneousPixel +template struct planar_pixel_reference; + +// Define the type of a mutable and read-only reference. (These typedefs are already provided by GIL) +typedef planar_pixel_reference< bits8&,rgb_t> rgb8_planar_ref_t; +typedef planar_pixel_reference rgb8c_planar_ref_t; +---- + +Note that, unlike the `pixel` struct, planar pixel references are templated +over the color space, not over the pixel layout. They always use a canonical +channel ordering. Ordering of their elements is unnecessary because their +elements are references to the channels. + +Sometimes the channels of a pixel may not be byte-aligned. For example an RGB +pixel in '5-5-6' format is a 16-bit pixel whose red, green and blue channels +occupy bits [0..4],[5..9] and [10..15] respectively. GIL provides a model for +such packed pixel formats: + +[source,cpp] +---- +// define an rgb565 pixel +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; + +function_requires >(); + +// rgb565 is compatible with bgr556. +function_requires >(); +---- + +In some cases, the pixel itself may not be byte aligned. For example, +consider an RGB pixel in '2-3-2' format. Its size is 7 bits. GIL +refers to such pixels, pixel iterators and images as +"bit-aligned". Bit-aligned pixels (and images) are more complex than +packed ones. Since packed pixels are byte-aligned, we can use a C++ +reference as the reference type to a packed pixel, and a C pointer as +an x_iterator over a row of packed pixels. For bit-aligned constructs +we need a special reference proxy class (bit_aligned_pixel_reference) +and iterator class (bit_aligned_pixel_iterator). The value type of +bit-aligned pixels is a packed_pixel. Here is how to use bit_aligned +pixels and pixel iterators: + +[source,cpp] +---- +// Mutable reference to a BGR232 pixel +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; + +// BGR232 pixel value. It is a packed_pixel of size 1 byte. (The last bit is unused) +typedef std::iterator_traits::value_type bgr232_pixel_t; +static_assert(sizeof(bgr232_pixel_t) == 1, ""); + +bgr232_pixel_t red(0,0,3); // = 0RRGGGBB, = 01100000 = 0x60 + +// a buffer of 7 bytes fits exactly 8 BGR232 pixels. +unsigned char pix_buffer[7]; +std::fill(pix_buffer,pix_buffer+7,0); + +// Fill the 8 pixels with red +bgr232_ptr_t pix_it(&pix_buffer[0],0); // start at bit 0 of the first pixel +for (int i=0; i<8; ++i) +{ + *pix_it++ = red; +} +// Result: 0x60 0x30 0x11 0x0C 0x06 0x83 0xC1 +---- + +== Algorithms + +Since pixels model `ColorBaseConcept` and `PixelBasedConcept` all +algorithms and metafunctions of color bases can work with them as well: + +[source,cpp] +---- +// This is how to access the first semantic channel (red) +assert(semantic_at_c<0>(rgb8) == semantic_at_c<0>(bgr8)); + +// This is how to access the red channel by name +assert(get_color(rgb8) == get_color(bgr8)); + +// This is another way of doing it (some compilers don't like the first one) +assert(get_color(rgb8,red_t()) == get_color(bgr8,red_t())); + +// This is how to use the PixelBasedConcept metafunctions +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, mp11::mp_list_c > )); + +// Pixels contain just the three channels and nothing extra +BOOST_MPL_ASSERT(sizeof(rgb8_pixel_t)==3); + +rgb8_planar_ref_t ref(bgr8); // copy construction is allowed from a compatible mutable pixel type + +get_color(ref) = 10; // assignment is ok because the reference is mutable +assert(get_color(bgr8)==10); // references modify the value they are bound to + +// Create a zero packed pixel and a full regular unpacked pixel. +rgb565_pixel_t r565; +rgb8_pixel_t rgb_full(255,255,255); + +// Convert all channels of the unpacked pixel to the packed one & assert the packed one is full +get_color(r565,red_t()) = channel_convert(get_color(rgb_full,red_t())); +get_color(r565,green_t()) = channel_convert(get_color(rgb_full,green_t())); +get_color(r565,blue_t()) = channel_convert(get_color(rgb_full,blue_t())); +assert(r565 == rgb565_pixel_t((uint16_t)65535)); +---- + +GIL also provides the `color_convert` algorithm to convert between pixels of +different color spaces and channel types: + +[source,cpp] +---- +rgb8_pixel_t red_in_rgb8(255,0,0); +cmyk16_pixel_t red_in_cmyk16; +color_convert(red_in_rgb8,red_in_cmyk16); +---- diff --git a/doc/modules/ROOT/pages/design/pixel_iterator.adoc b/doc/modules/ROOT/pages/design/pixel_iterator.adoc new file mode 100644 index 0000000000..a2c90b74a0 --- /dev/null +++ b/doc/modules/ROOT/pages/design/pixel_iterator.adoc @@ -0,0 +1,299 @@ += Pixel Iterator + +== Overview + +Pixel iterators are random traversal iterators whose `value_type +models` `PixelValueConcept`. + +== Fundamental Iterator + +Pixel iterators provide metafunctions to determine whether they are mutable +(i.e. whether they allow for modifying the pixel they refer to), to get the +immutable (read-only) type of the iterator, and to determine whether they are +plain iterators or adaptors over another pixel iterator: + +[source,cpp] +---- +concept PixelIteratorConcept + : PixelBasedConcept +{ + where PixelValueConcept; + typename const_iterator_type::type; + where PixelIteratorConcept::type>; + static const bool iterator_is_mutable::value; + static const bool is_iterator_adaptor::value; // is it an iterator adaptor +}; + +template +concept MutablePixelIteratorConcept : PixelIteratorConcept, MutableRandomAccessIteratorConcept {}; +---- + +[NOTE] +==== +See also: + +* link:../reference/group___pixel_iterator_concept_pixel_iterator.html[PixelIteratorConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_pixel_iterator_concept.html[MutablePixelIteratorConcept] +==== + +=== Models + +A built-in pointer to pixel, `pixel*`, is GIL model for +pixel iterator over interleaved homogeneous pixels. Similarly, +`packed_pixel*` is GIL model for an iterator +over interleaved packed pixels. + +For planar homogeneous pixels, GIL provides the class +`planar_pixel_iterator`, templated over a channel iterator and color space. +Here is how the standard mutable and read-only planar RGB iterators over +unsigned char are defined: + +[source,cpp] +---- +template +struct planar_pixel_iterator; + +// GIL provided typedefs +typedef planar_pixel_iterator rgb8c_planar_ptr_t; +typedef planar_pixel_iterator< bits8*, rgb_t> rgb8_planar_ptr_t; +---- + +`planar_pixel_iterator` also models `HomogeneousColorBaseConcept` (it +subclasses from `homogeneous_color_base`) and, as a result, all color base +algorithms apply to it. The element type of its color base is a channel +iterator. For example, GIL implements `operator++` of planar iterators +approximately like this: + +[source,cpp] +---- +template +struct inc : public std::unary_function +{ + T operator()(T x) const { return ++x; } +}; + +template +planar_pixel_iterator& +planar_pixel_iterator::operator++() +{ + static_transform(*this,*this,inc()); + return *this; +} +---- + +Since `static_transform` uses compile-time recursion, incrementing an +instance of `rgb8_planar_ptr_t` amounts to three pointer increments. +GIL also uses the class `bit_aligned_pixel_iterator` as a model for a pixel +iterator over bit-aligned pixels. Internally it keeps track of the current +byte and the bit offset. + +== Iterator Adaptor + +Iterator adaptor is an iterator that wraps around another iterator. Its +`is_iterator_adaptor` metafunction must evaluate to true, and it needs to +provide a member method to return the base iterator, a metafunction to get its +type, and a metafunction to rebind to another base iterator: + +[source,cpp] +---- +concept IteratorAdaptorConcept +{ + where SameType::type, mp11::mp_true>; + + typename iterator_adaptor_get_base; + where Metafunction >; + where boost_concepts::ForwardTraversalConcept::type>; + + typename another_iterator; + typename iterator_adaptor_rebind::type; + where boost_concepts::ForwardTraversalConcept; + where IteratorAdaptorConcept::type>; + + const iterator_adaptor_get_base::type& Iterator::base() const; +}; + +template +concept MutableIteratorAdaptorConcept : IteratorAdaptorConcept {}; +---- + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_iterator_adaptor_concept.html[IteratorAdaptorConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_iterator_adaptor_concept.html[MutableIteratorAdaptorConcept] +==== + +=== Models + +GIL provides several models of `IteratorAdaptorConcept`: + +- `memory_based_step_iterator`: An iterator adaptor that changes + the fundamental step of the base iterator + (see xref:design/pixel_iterator.adoc#_step_iterator[Step Iterator]) + +- `dereference_iterator_adaptor`: An iterator that applies a + unary function `Fn` upon dereferencing. It is used, for example, for + on-the-fly color conversion. It can be used to construct a shallow image + "view" that pretends to have a different color space or channel depth. + See xref:design/image_view.adoc[image_view] for more. The unary function `Fn` must + model `PixelDereferenceAdaptorConcept` (see below). + +== Pixel Dereference Adaptor + +Pixel dereference adaptor is a unary function that can be applied upon +dereferencing a pixel iterator. Its argument type could be anything (usually a +`PixelConcept`) and the result type must be convertible to `PixelConcept`: + +[source,cpp] +---- +template +concept PixelDereferenceAdaptorConcept: + DefaultConstructibleConcept, + CopyConstructibleConcept, + AssignableConcept +{ + typename const_t; where PixelDereferenceAdaptorConcept; + typename value_type; where PixelValueConcept; + typename reference; where PixelConcept::type>; // may be mutable + typename const_reference; // must not be mutable + static const bool D::is_mutable; + + where Convertible; +}; +---- + +=== Models + +GIL provides several models of `PixelDereferenceAdaptorConcept`: + +* `color_convert_deref_fn`: a function object that performs color conversion + +* `detail::nth_channel_deref_fn`: a function object that returns a grayscale + pixel corresponding to the n-th channel of a given pixel + +* `deref_compose`: a function object that composes two models of + `PixelDereferenceAdaptorConcept`. Similar to `std::unary_compose`, + except it needs to pull the additional typedefs required by + `PixelDereferenceAdaptorConcept` + +GIL uses pixel dereference adaptors to implement image views that perform +color conversion upon dereferencing, or that return the N-th channel of the +underlying pixel. They can be used to model virtual image views that perform +an arbitrary function upon dereferencing, for example a view of the Mandelbrot +set. `dereference_iterator_adaptor` is an iterator wrapper over +a pixel iterator `Iterator` that invokes the given dereference iterator +adaptor `Fn` upon dereferencing. + +[#_step_iterator] +== Step Iterator + +Sometimes we want to traverse pixels with a unit step other than the one +provided by the fundamental pixel iterators. Examples where this would be +useful: + +* a single-channel view of the red channel of an RGB interleaved image +* left-to-right flipped image (step = -fundamental_step) +* subsampled view, taking every N-th pixel (step = N*fundamental_step) +* traversal in vertical direction (step = number of bytes per row) +* any combination of the above (steps are multiplied) + +Step iterators are forward traversal iterators that allow changing the step +between adjacent values: + +[source,cpp] +---- +concept StepIteratorConcept +{ + template void Iterator::set_step(D step); +}; + +concept MutableStepIteratorConcept + : StepIteratorConcept +{}; +---- + +GIL currently provides a step iterator whose `value_type models` +`PixelValueConcept`. In addition, the step is specified in memory units +(which are bytes or bits). This is necessary, for example, when implementing +an iterator navigating along a column of pixels - the size of a row of pixels +may sometimes not be divisible by the size of a pixel; for example rows may be +word-aligned. + +To advance in bytes/bits, the base iterator must model +`MemoryBasedIteratorConcept`. A memory-based iterator has an inherent memory +unit, which is either a bit or a byte. It must supply functions returning the +number of bits per memory unit (1 or 8), the current step in memory units, the +memory-unit distance between two iterators, and a reference a given distance +in memunits away. It must also supply a function that advances an iterator a +given distance in memory units. `memunit_advanced` and +`memunit_advanced_ref` have a default implementation but some iterators may +supply a more efficient version: + +[source,cpp] +---- +concept MemoryBasedIteratorConcept +< + boost_concepts::RandomAccessTraversalConcept Iterator +> +{ + typename byte_to_memunit; where metafunction >; + std::ptrdiff_t memunit_step(const Iterator&); + std::ptrdiff_t memunit_distance(const Iterator& , const Iterator&); + void memunit_advance(Iterator&, std::ptrdiff_t diff); + Iterator memunit_advanced(const Iterator& p, std::ptrdiff_t diff) { Iterator tmp; memunit_advance(tmp,diff); return tmp; } + Iterator::reference memunit_advanced_ref(const Iterator& p, std::ptrdiff_t diff) { return *memunit_advanced(p,diff); } +}; +---- + +It is useful to be able to construct a step iterator over another iterator. +More generally, given a type, we want to be able to construct an equivalent +type that allows for dynamically specified horizontal step: + +[source,cpp] +---- +concept HasDynamicXStepTypeConcept +{ + typename dynamic_x_step_type; + where Metafunction >; +}; +---- + +All models of pixel iterators, locators and image views that GIL provides +support `HasDynamicXStepTypeConcept`. + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_step_iterator_concept.html[StepIteratorConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_step_iterator_concept.html[MutableStepIteratorConcept] +* link:../reference/structboost_1_1gil_1_1_memory_based_iterator_concept.html[MemoryBasedIteratorConcept] +* link:../reference/structboost_1_1gil_1_1_has_dynamic_x_step_type_concept.html[HasDynamicXStepTypeConcept] +==== + +=== Models + +All standard memory-based iterators GIL currently provides model +`MemoryBasedIteratorConcept`. GIL provides the class +`memory_based_step_iterator` which models `PixelIteratorConcept`, +`StepIteratorConcept`, and `MemoryBasedIteratorConcept`. It takes the base +iterator as a template parameter (which must model `PixelIteratorConcept` +and `MemoryBasedIteratorConcept`) and allows changing the step dynamically. +GIL implementation contains the base iterator and a `ptrdiff_t` denoting the +number of memory units (bytes or bits) to skip for a unit step. It may also be +used with a negative number. GIL provides a function to create a step iterator +from a base iterator and a step: + +[source,cpp] +---- +// Iterator models MemoryBasedIteratorConcept, HasDynamicXStepTypeConcept +template +typename dynamic_x_step_type::type make_step_iterator(Iterator const& it, std::ptrdiff_t step); +---- + +GIL also provides a model of an iterator over a virtual array of pixels, +`position_iterator`. It is a step iterator that keeps track of the pixel +position and invokes a function object to get the value of the pixel upon +dereferencing. It models `PixelIteratorConcept` and `StepIteratorConcept` +but not `MemoryBasedIteratorConcept`. diff --git a/doc/modules/ROOT/pages/design/pixel_locator.adoc b/doc/modules/ROOT/pages/design/pixel_locator.adoc new file mode 100644 index 0000000000..458984fded --- /dev/null +++ b/doc/modules/ROOT/pages/design/pixel_locator.adoc @@ -0,0 +1,306 @@ += Pixel Locator + +== Overview + +A Locator allows for navigation in two or more dimensions. Locators are +N-dimensional iterators in spirit, but we use a different name because they +don't satisfy all the requirements of iterators. For example, they don't +supply increment and decrement operators because it is unclear which dimension +the operators should advance along. +N-dimensional locators model the following concept: + +[source,cpp] +---- +concept RandomAccessNDLocatorConcept +{ + typename value_type; // value over which the locator navigates + typename reference; // result of dereferencing + typename difference_type; where PointNDConcept; // return value of operator-. + typename const_t; // same as Loc, but operating over immutable values + typename cached_location_t; // type to store relative location (for efficient repeated access) + typename point_t = difference_type; + + static const size_t num_dimensions; // dimensionality of the locator + where num_dimensions = point_t::num_dimensions; + + // The difference_type and iterator type along each dimension. The iterators may only differ in + // difference_type. Their value_type must be the same as Loc::value_type + template struct axis { + typename coord_t = point_t::axis::coord_t; + typename iterator; where RandomAccessTraversalConcept; // iterator along D-th axis. + where iterator::value_type == value_type; + }; + + // Defines the type of a locator similar to this type, except it invokes Deref upon dereferencing + template struct add_deref { + typename type; where RandomAccessNDLocatorConcept; + static type make(const Loc& loc, const Deref& deref); + }; + + Loc& operator+=(Loc&, const difference_type&); + Loc& operator-=(Loc&, const difference_type&); + Loc operator+(const Loc&, const difference_type&); + Loc operator-(const Loc&, const difference_type&); + + reference operator*(const Loc&); + reference operator[](const Loc&, const difference_type&); + + // Storing relative location for faster repeated access and accessing it + cached_location_t Loc::cache_location(const difference_type&) const; + reference operator[](const Loc&,const cached_location_t&); + + // Accessing iterators along a given dimension at the current location or at a given offset + template axis::iterator& Loc::axis_iterator(); + template axis::iterator const& Loc::axis_iterator() const; + template axis::iterator Loc::axis_iterator(const difference_type&) const; +}; + +template +concept MutableRandomAccessNDLocatorConcept + : RandomAccessNDLocatorConcept +{ + where Mutable; +}; +---- + +Two-dimensional locators have additional requirements: + +[source,cpp] +---- +concept RandomAccess2DLocatorConcept +{ + where num_dimensions==2; + where Point2DConcept; + + typename x_iterator = axis<0>::iterator; + typename y_iterator = axis<1>::iterator; + typename x_coord_t = axis<0>::coord_t; + typename y_coord_t = axis<1>::coord_t; + + // Only available to locators that have dynamic step in Y + //Loc::Loc(const Loc& loc, y_coord_t); + + // Only available to locators that have dynamic step in X and Y + //Loc::Loc(const Loc& loc, x_coord_t, y_coord_t, bool transposed=false); + + x_iterator& Loc::x(); + x_iterator const& Loc::x() const; + y_iterator& Loc::y(); + y_iterator const& Loc::y() const; + + x_iterator Loc::x_at(const difference_type&) const; + y_iterator Loc::y_at(const difference_type&) const; + Loc Loc::xy_at(const difference_type&) const; + + // x/y versions of all methods that can take difference type + x_iterator Loc::x_at(x_coord_t, y_coord_t) const; + y_iterator Loc::y_at(x_coord_t, y_coord_t) const; + Loc Loc::xy_at(x_coord_t, y_coord_t) const; + reference operator()(const Loc&, x_coord_t, y_coord_t); + cached_location_t Loc::cache_location(x_coord_t, y_coord_t) const; + + bool Loc::is_1d_traversable(x_coord_t width) const; + y_coord_t Loc::y_distance_to(const Loc& loc2, x_coord_t x_diff) const; +}; + +concept MutableRandomAccess2DLocatorConcept + : MutableRandomAccessNDLocatorConcept {}; +---- + +2D locators can have a dynamic step not just horizontally, but +vertically. This gives rise to the Y equivalent of +`HasDynamicXStepTypeConcept`: + +[source,cpp] +---- +concept HasDynamicYStepTypeConcept +{ + typename dynamic_y_step_type; + where Metafunction >; +}; +---- + +All locators and image views that GIL provides model +`HasDynamicYStepTypeConcept`. + +Sometimes it is necessary to swap the meaning of X and Y for a given locator +or image view type (for example, GIL provides a function to transpose an image +view). Such locators and views must be transposable: + +[source,cpp] +---- +concept HasTransposedTypeConcept +{ + typename transposed_type; + where Metafunction >; +}; +---- + +All GIL provided locators and views model `HasTransposedTypeConcept`. + +The locators GIL uses operate over models of `PixelConcept` and their x and +y dimension types are the same. They model the following concept: + +[source,cpp] +---- +concept PixelLocatorConcept +{ + where PixelValueConcept; + where PixelIteratorConcept; + where PixelIteratorConcept; + where x_coord_t == y_coord_t; + + typename coord_t = x_coord_t; +}; + +concept MutablePixelLocatorConcept : MutableRandomAccess2DLocatorConcept {}; +---- + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_has_dynamic_y_step_type_concept.html[HasDynamicYStepTypeConcept] +* link:../reference/structboost_1_1gil_1_1_has_transposed_type_concept.html[HasTransposedTypeConcept] +* link:../reference/structboost_1_1gil_1_1_random_access_n_d_locator_concept.html[RandomAccessNDLocatorConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_random_access_n_d_locator_concept.html[MutableRandomAccessNDLocatorConcept] +* link:../reference/structboost_1_1gil_1_1_random_access2_d_locator_concept.html[RandomAccess2DLocatorConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_random_access2_d_locator_concept.html[MutableRandomAccess2DLocatorConcept] +* link:../reference/structboost_1_1gil_1_1_pixel_locator_concept.html[PixelLocatorConcept] +* link:../reference/structboost_1_1gil_1_1_mutable_pixel_locator_concept.html[MutablePixelLocatorConcept] +==== + +== Models + +GIL provides two models of `PixelLocatorConcept` - a memory-based locator, +`memory_based_2d_locator` and a virtual locator `virtual_2d_locator`. + +The `memory_based_2d_locator` is a locator over planar or interleaved images +that have their pixels in memory. It takes a model of `StepIteratorConcept` +over pixels as a template parameter. (When instantiated with a model of +`MutableStepIteratorConcept`, it models `MutablePixelLocatorConcept`.) + +[source,cpp] +---- +// StepIterator models StepIteratorConcept, MemoryBasedIteratorConcept +template +class memory_based_2d_locator; +---- + +The step of `StepIterator` must be the number of memory units (bytes or +bits) per row (thus it must be memunit advanceable). The class +`memory_based_2d_locator` is a wrapper around `StepIterator` and uses it +to navigate vertically, while its base iterator is used to navigate +horizontally. + +Combining fundamental iterator and step iterator allows us to create locators +that describe complex pixel memory organizations. First, we have a choice of +iterator to use for horizontal direction, i.e. for iterating over the pixels +on the same row. Using the fundamental and step iterators gives us four +choices: + +- `pixel*` - for interleaved images +- `planar_pixel_iterator` - for planar images +- `memory_based_step_iterator*>` - for interleaved images with + non-standard step +- `memory_based_step_iterator >` - for planar + images with non-standard step + +Of course, one could provide their own custom x-iterator. One such example +described later is an iterator adaptor that performs color conversion when +dereferenced. + +Given a horizontal iterator `XIterator`, we could choose the `y-iterator`, +the iterator that moves along a column, as +`memory_based_step_iterator` with a step equal to the number of +memory units (bytes or bits) per row. Again, one is free to provide their own +y-iterator. + +Then we can instantiate +`memory_based_2d_locator >` to obtain +a 2D pixel locator, as the diagram indicates: + +image::pixel_locator.gif[] + +The `memory_based_2d_locator` also offers `cached_location_t` as mechanism +to store relative locations for optimized repeated access of neighborhood +pixels. The 2D coordinates of relative locations are cached as 1-dimensional +raw byte offsets. This provides efficient access if neighboring locations +relative to a given locator are read or written frequently (e.g. in filters). + +The `virtual_2d_locator` is a locator that is instantiated with a function +object invoked upon dereferencing a pixel. It returns the value of a pixel +given its X,Y coordinates. Virtual locators can be used to implement virtual +image views that can model any user-defined function. See the GIL tutorial for +an example of using virtual locators to create a view of the Mandelbrot set. + +Both the virtual and the memory-based locators subclass from +`pixel_2d_locator_base`, a base class that provides most of the interface +required by `PixelLocatorConcept`. Users may find this base class useful if +they need to provide other models of `PixelLocatorConcept`. + +Here is some sample code using locators: + +[source,cpp] +---- +loc=img.xy_at(10,10); // start at pixel (x=10,y=10) +above=loc.cache_location(0,-1); // remember relative locations of neighbors above and below +below=loc.cache_location(0, 1); +++loc.x(); // move to (11,10) +loc.y()+=15; // move to (11,25) +loc-=point(1,1);// move to (10,24) +*loc=(loc(0,-1)+loc(0,1))/2; // set pixel (10,24) to the average of (10,23) and (10,25) (grayscale pixels only) +*loc=(loc[above]+loc[below])/2; // the same, but faster using cached relative neighbor locations +---- + +The standard GIL locators are fast and lightweight objects. For example, the +locator for a simple interleaved image consists of one raw pointer to the +pixel location plus one integer for the row size in bytes, for a total of +8 bytes. `++loc.x()` amounts to incrementing a raw pointer (or N pointers +for planar images). Computing 2D offsets is slower as it requires +multiplication and addition. Filters, for example, need to access the same +neighbors for every pixel in the image, in which case the relative positions +can be cached into a raw byte difference using `cache_location`. +In the above example `loc[above]` for simple interleaved images amounts to a +raw array index operator. + +== Iterator over 2D image + +Sometimes we want to perform the same, location-independent operation +over all pixels of an image. In such a case it is useful to represent +the pixels as a one-dimensional array. GIL's `iterator_from_2d` is a +random access traversal iterator that visits all pixels in an image in +the natural memory-friendly order left-to-right inside +top-to-bottom. It takes a locator, the width of the image and the +current X position. This is sufficient information for it to determine +when to do a "carriage return". Synopsis: + +[source,cpp] +---- +template // Models PixelLocatorConcept +class iterator_from_2d +{ +public: + iterator_from_2d(const Locator& loc, int x, int width); + + iterator_from_2d& operator++(); // if (++_x<_width) ++_p.x(); else _p+=point_t(-_width,1); + + ... +private: + int _x, _width; + Locator _p; +}; +---- + +Iterating through the pixels in an image using `iterator_from_2d` is slower +than going through all rows and using the x-iterator at each row. This is +because two comparisons are done per iteration step - one for the end +condition of the loop using the iterators, and one inside +`iterator_from_2d::operator++` to determine whether we are at the end of a +row. For fast operations, such as pixel copy, this second check adds about +15% performance delay (measured for interleaved images on Intel platform). +GIL overrides some STL algorithms, such as `std::copy` and `std::fill`, +when invoked with `iterator_from_2d`-s, to go through each row using their +base x-iterators, and, if the image has no padding (i.e. +`iterator_from_2d::is_1d_traversable()` returns true), to simply iterate +using the x-iterators directly. diff --git a/doc/modules/ROOT/pages/design/point.adoc b/doc/modules/ROOT/pages/design/point.adoc new file mode 100644 index 0000000000..f0f8a5c8b3 --- /dev/null +++ b/doc/modules/ROOT/pages/design/point.adoc @@ -0,0 +1,54 @@ += Point + +== Overview + +A point defines the location of a pixel inside an image. It can also be used +to describe the dimensions of an image. In most general terms, points are +N-dimensional and model the following concept: + +[source,cpp] +---- +concept PointNDConcept : Regular +{ + // the type of a coordinate along each axis + template struct axis; where Metafunction; + + const size_t num_dimensions; + + // accessor/modifier of the value of each axis. + template const typename axis::type& T::axis_value() const; + template typename axis::type& T::axis_value(); +}; +---- + +GIL uses a two-dimensional point, which is a refinement of `PointNDConcept` +in which both dimensions are of the same type: + +[source,cpp] +---- +concept Point2DConcept : PointNDConcept +{ + where num_dimensions == 2; + where SameType::type, axis<1>::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 x,y; +}; +---- + +[NOTE] +==== +See also: + +* link:../reference/structboost_1_1gil_1_1_point_n_d_concept.html[PointNDConcept] +* link:../reference/structboost_1_1gil_1_1_point2_d_concept.html[Point2DConcept] +==== + +== Models + +GIL provides a model of `Point2DConcept`, `point` where `T` is the +coordinate type. diff --git a/doc/modules/ROOT/pages/design/technicalities.adoc b/doc/modules/ROOT/pages/design/technicalities.adoc new file mode 100644 index 0000000000..86a0358a11 --- /dev/null +++ b/doc/modules/ROOT/pages/design/technicalities.adoc @@ -0,0 +1,88 @@ += Technicalities + +== Creating a reference proxy + +Sometimes it is necessary to create a proxy class that represents a +reference to a given object. Examples of these are GIL's reference to +a planar pixel (`planar_pixel_reference`) and GIL's sub-byte channel +references. Writing a reference proxy class can be tricky. One problem +is that the proxy reference is constructed as a temporary object and +returned by value upon dereferencing the iterator: + +[source,cpp] +---- +struct rgb_planar_pixel_iterator +{ + typedef my_reference_proxy reference; + reference operator*() const { return reference(red,green,blue); } +}; +---- + +The problem arises when an iterator is dereferenced directly into a +function that takes a mutable pixel: + +[source,cpp] +---- +template // Models MutablePixelConcept +void invert_pixel(Pixel& p); + +rgb_planar_pixel_iterator myIt; +invert_pixel(*myIt); // compile error! +---- + +C++ does not allow for matching a temporary object against a non-constant +reference. The solution is to: + +* Use const qualifier on all members of the reference proxy object: + +[source,cpp] +---- +template +struct my_reference_proxy +{ + const my_reference_proxy& operator=(const my_reference_proxy& p) const; + const my_reference_proxy* operator->() const { return this; } + ... +}; +---- + +* Use different classes to denote mutable and constant reference + (maybe based on the constness of the template parameter) + +* Define the reference type of your iterator with const qualifier: + +[source,cpp] +---- +struct iterator_traits +{ + typedef const my_reference_proxy reference; +}; +---- + +A second important issue is providing an overload for `swap` for +your reference class. The default `std::swap` will not work +correctly. You must use a real value type as the temporary. A further +complication is that in some implementations of the STL the `swap` +function is incorrectly called qualified, as `std::swap`. The only +way for these STL algorithms to use your overload is if you define it +in the `std` namespace: + +[source,cpp] +---- +namespace std +{ + template + void swap(my_reference_proxy& x, my_reference_proxy& y) + { + my_value tmp=x; + x=y; + y=tmp; + } +} +---- + +Lastly, remember that constructors and copy-constructors of proxy +references are always shallow and assignment operators are deep. + +We are grateful to Dave Abrahams, Sean Parent and Alex Stepanov for +suggesting the above solution. diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc new file mode 100644 index 0000000000..9a4faeb46b --- /dev/null +++ b/doc/modules/ROOT/pages/examples.adoc @@ -0,0 +1,26 @@ += Examples + +The following example programs, shipped with GIL, demonstrate various features of the library. + +* link:https://github.com/boostorg/gil/blob/develop/example/x_gradient.cpp[x_gradient.cpp]: + Writing an algorithm that operates on generic images +* link:https://github.com/boostorg/gil/blob/develop/example/dynamic_image.cpp[dynamic_image.cpp]: + Using images whose properties (color space, channel type) are specified + at run time +* link:https://github.com/boostorg/gil/blob/develop/example/histogram.cpp[histogram.cpp]: Creating a histogram +* link:https://github.com/boostorg/gil/blob/develop/example/interleaved_ptr.cpp[interleaved_ptr.cpp], + link:https://github.com/boostorg/gil/blob/develop/example/interleaved_ptr.hpp[interleaved_ptr.hpp], + link:https://github.com/boostorg/gil/blob/develop/example/interleaved_ref.hpp[interleaved_ref.hpp]: + Creating your own pixel reference and pixel iterator +* link:https://github.com/boostorg/gil/blob/develop/example/mandelbrot.cpp[mandelbrot.cpp]: + Creating a synthetic image defined by a function +* link:https://github.com/boostorg/gil/blob/develop/example/packed_pixel.cpp[packed_pixel.cpp]: + Defining bitmasks and images whose channels or pixels are not byte-aligned +* link:https://github.com/boostorg/gil/blob/develop/example/resize.cpp[resize.cpp]: + Rescaling an image using bilinear sampling (requires the optional + Numeric extension) +* link:https://github.com/boostorg/gil/blob/develop/example/affine.cpp[affine.cpp]: + Applying an affine transformation to an image (requires the optional + Numeric extension) +* link:https://github.com/boostorg/gil/blob/develop/example/convolution.cpp[convolution.cpp]: + Blurring images (requires the optional Numeric extension) diff --git a/doc/modules/ROOT/pages/histogram/create.adoc b/doc/modules/ROOT/pages/histogram/create.adoc new file mode 100644 index 0000000000..65fcce8036 --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/create.adoc @@ -0,0 +1,43 @@ +[#create_histogram] += Create a histogram + +*Method 1* - Using the histogram constructor + +Syntax: + +[source,cpp] +---- +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: + +[source,cpp] +---- +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: + +[source,cpp] +---- +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/modules/ROOT/pages/histogram/cumulative.adoc similarity index 55% rename from doc/histogram/cumulative.rst rename to doc/modules/ROOT/pages/histogram/cumulative.adoc index 29c088207b..a5454df900 100644 --- a/doc/histogram/cumulative.rst +++ b/doc/modules/ROOT/pages/histogram/cumulative.adoc @@ -1,29 +1,27 @@ -.. _cumulative_histogram: +[#cumulative_histogram] += Making a cumulative histogram -Making a cumulative histogram -============================= - -Overview --------- +== 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 existent in the histogram. -The GIL histogram class has the ability to convert itself into its cumulative version. +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. +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 +[source,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_. diff --git a/doc/modules/ROOT/pages/histogram/extend.adoc b/doc/modules/ROOT/pages/histogram/extend.adoc new file mode 100644 index 0000000000..7eb2ce1270 --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/extend.adoc @@ -0,0 +1,62 @@ +[#extend_support] += Extending the class + +== User defined Axes + +In case you need a histogram with an axis of an arbitrary type that is not identified by +the C++ Standard Library, you need to provide an 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. + +[source,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 let's get to the usage example. + +[source,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()* ++ +[source,cpp] +---- +// Demo for std::vector +std::vector v; +/* +Fill vector... +*/ +gil::cumulative_histogram(v); +---- diff --git a/doc/modules/ROOT/pages/histogram/fill.adoc b/doc/modules/ROOT/pages/histogram/fill.adoc new file mode 100644 index 0000000000..da9fe256ed --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/fill.adoc @@ -0,0 +1,98 @@ +[#fill_it] += Fill histogram + +== 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 ++ +[source,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 ++ +[source,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 ++ +[source,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 ++ +[source,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 ++ +[source,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/modules/ROOT/pages/histogram/index.adoc b/doc/modules/ROOT/pages/histogram/index.adoc new file mode 100644 index 0000000000..8483bc260c --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/index.adoc @@ -0,0 +1,4 @@ += Histogram + +The GIL documentation sections listed below are dedicated to describe the +histogram class and functions used in many image processing algorithms. diff --git a/doc/modules/ROOT/pages/histogram/limitations.adoc b/doc/modules/ROOT/pages/histogram/limitations.adoc new file mode 100644 index 0000000000..e23d2c3539 --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/limitations.adoc @@ -0,0 +1,4 @@ +[#limitations] += Limitations + +_TODO_ diff --git a/doc/modules/ROOT/pages/histogram/overview.adoc b/doc/modules/ROOT/pages/histogram/overview.adoc new file mode 100644 index 0000000000..09cb5c8f18 --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/overview.adoc @@ -0,0 +1,22 @@ += Overview + +== 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: + +. xref:histogram/create.adoc[] +. xref:histogram/fill.adoc[] +. xref:histogram/subhistogram.adoc[] +. xref:histogram/cumulative.adoc[] +. xref:histogram/stl_compatibility.adoc[] +. xref:histogram/extend.adoc[] +. xref:histogram/limitations.adoc[] + +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/modules/ROOT/pages/histogram/stl_compatibility.adoc b/doc/modules/ROOT/pages/histogram/stl_compatibility.adoc new file mode 100644 index 0000000000..eac75b31b2 --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/stl_compatibility.adoc @@ -0,0 +1,4 @@ +[#stl_compatibility] += STL compatibility + +_TODO_ diff --git a/doc/modules/ROOT/pages/histogram/subhistogram.adoc b/doc/modules/ROOT/pages/histogram/subhistogram.adoc new file mode 100644 index 0000000000..47f4e09cf6 --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/subhistogram.adoc @@ -0,0 +1,65 @@ +[#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 ++ +[source,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: ++ +[source,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 ++ +[source,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: ++ +[source,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/modules/ROOT/pages/histogram/utilities.adoc b/doc/modules/ROOT/pages/histogram/utilities.adoc new file mode 100644 index 0000000000..1b6bbb3e82 --- /dev/null +++ b/doc/modules/ROOT/pages/histogram/utilities.adoc @@ -0,0 +1,4 @@ +[#utilities] += Utilities + +_TODO_ diff --git a/doc/modules/ROOT/pages/image_processing/affine-region-detectors.adoc b/doc/modules/ROOT/pages/image_processing/affine-region-detectors.adoc new file mode 100644 index 0000000000..f4df717f3e --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/affine-region-detectors.adoc @@ -0,0 +1,103 @@ += Affine region detectors + +== What is being detected? + +Affine region is basically any region of the image +that is stable under affine transformations. It can be +edges under affinity conditions, corners (small patch of an image) +or any other stable features. + +''' + +== Available detectors + +At the moment, the following detectors are implemented + +* Harris detector + +* Hessian detector + +''' + +== Algorithm steps + +=== Harris and Hessian + +Both are derived from a concept called Moravec window. Let's have a look +at the image below: + +image::Moravec-window-corner.png[Moravec window corner case] + +As can be noticed, moving the yellow window in any direction will cause +very big change in intensity. Now, let's have a look at the edge case: + +image::Moravec-window-edge.png[Moravec window edge case] + +In this case, intensity change will happen only when moving in +particular direction. + +This is the key concept in understanding how the two corner detectors +work. + +The algorithms have the same structure: + +. Compute image derivatives + +. Compute weighted sum + +. Compute response + +. Threshold (optional) + +Harris and Hessian differ in what *derivatives they compute*. Harris +computes the following derivatives: + +`HarrisMatrix = [(dx)^2, dxdy], [dxdy, (dy)^2]` + +(note that `d(x^2)` and `(dy^2)` are *numerical* powers, not gradient again). + +The three distinct terms of a matrix can be separated into three images, +to simplify implementation. Hessian, on the other hand, computes second +order derivatives: + +`HessianMatrix = [dxdx, dxdy][dxdy, dydy]` + +*Weighted sum* is the same for both. Usually Gaussian blur +matrix is used as weights, because corners should have hill like +curvature in gradients, and other weights might be noisy. +Basically overlay weights matrix over a corner, compute sum of +`s[i,j]=image[x + i, y + j] * weights[i, j]` for `i, j` +from zero to weight matrix dimensions, then move the window +and compute again until all of the image is covered. + +*Response computation* is a matter of choice. Given the general form +of both matrices above + +`[a, b][c, d]` + +One of the response functions is + +`response = det - k * trace^2 = a * c - b * d - k * (a + d)^2` + +`k` is called discrimination constant. Usual values are `0.04` - +`0.06`. + +The other is simply determinant + +`response = det = a * c - b * d` + +*Thresholding* is optional, but without it the result will be +extremely noisy. For complex images, like the ones of outdoors, for +Harris it will be in order of 100000000 and for Hessian will be in order +of 10000. For simpler images values in order of 100s and 1000s should be +enough. The numbers assume `uint8_t` gray image. + +To get a deeper explanation please refer to the following *papers*: + +http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.434.4816&rep=rep1&type=pdf[Harris, Christopher G., and Mike Stephens. "A combined corner and edge +detector." In Alvey vision conference, vol. 15, no. 50, pp. 10-5244. +1988.] + +https://hal.inria.fr/inria-00548252/document[Mikolajczyk, Krystian, and Cordelia Schmid. "An affine invariant interest point detector." In European conference on computer vision, pp. 128-142. Springer, Berlin, Heidelberg, 2002.] + +https://hal.inria.fr/inria-00548528/document[Mikolajczyk, Krystian, Tinne Tuytelaars, Cordelia Schmid, Andrew Zisserman, Jiri Matas, Frederik Schaffalitzky, Timor Kadir, and Luc Van Gool. "A comparison of affine region detectors." International journal of computer vision 65, no. 1-2 (2005): 43-72.] diff --git a/doc/image_processing/basics.rst b/doc/modules/ROOT/pages/image_processing/basics.adoc similarity index 87% rename from doc/image_processing/basics.rst rename to doc/modules/ROOT/pages/image_processing/basics.adoc index 15ff3546ba..4634d6a2a3 100644 --- a/doc/image_processing/basics.rst +++ b/doc/modules/ROOT/pages/image_processing/basics.adoc @@ -1,27 +1,24 @@ -Basics ------- += Basics Here are basic concepts that might help to understand documentation written in this folder: -Convolution -~~~~~~~~~~~ +== Convolution The simplest way to look at this is "tweaking the input so that it would look like the shape provided". What exact tweaking is applied depends on the kernel. --------------- +''' -Filters, kernels, weights -~~~~~~~~~~~~~~~~~~~~~~~~~ +== Filters, kernels, weights Those three words usually mean the same thing, unless context is clear about a different usage. Simply put, they are matrices, that are used to achieve certain effects on the image. Let's consider a simple one, 3 by 3 Scharr filter -``ScharrX = [1,0,-1][1,0,-1][1,0,-1]`` +`ScharrX = [1,0,-1][1,0,-1][1,0,-1]` The filter above, when convolved with a single channel image (intensity/luminance strength), will produce a gradient in X @@ -30,19 +27,17 @@ kernel though, and one good example is median filter (mean is the arithmetic mean, whereas median will be the center element of a sorted array). --------------- +''' -Derivatives -~~~~~~~~~~~ +== Derivatives A derivative of an image is a gradient in one of two directions: x (horizontal) and y (vertical). To compute a derivative, one can use Scharr, Sobel and other gradient filters. --------------- +''' -Curvature -~~~~~~~~~ +== Curvature The word, when used alone, will mean the curvature that would be generated if values of an image would be plotted in 3D graph. X and Z diff --git a/doc/modules/ROOT/pages/image_processing/contrast_enhancement/histogram_equalization.adoc b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/histogram_equalization.adoc new file mode 100644 index 0000000000..e71f40b143 --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/histogram_equalization.adoc @@ -0,0 +1,76 @@ +[#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. + +image::he_chart.png[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~x~) p~x~ in [0, 255], then apply + the transformation p~x'~ = H(p~x~), p~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~x'~) = p~x'~ . + +Hence, + +=> H(p~x'~) = H(p~x~) + +=> p~x'~ = H(p~x~) + +== Results + +The algorithm is applied on a few standard images. One of the transformations in shown below: + +*Grayscale Image* + +image::https://github.com/boost-gil/test-images/blob/master/jpeg/suite/barbara.jpg[Could not load image.] + +*RGB* + +image::https://github.com/boost-gil/test-images/blob/master/jpeg/suite/church.jpg[Could not load image.] + +== Demo + +Usage Syntax: + +[source,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/doc/modules/ROOT/pages/image_processing/contrast_enhancement/histogram_matching.adoc b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/histogram_matching.adoc new file mode 100644 index 0000000000..b38d63e455 --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/histogram_matching.adoc @@ -0,0 +1,69 @@ +[#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~x~) and of reference image is R(p~x'~) + p~x~ & p~x'~ in [0, 255], then apply the transformation + p~x'~ = R^-1^(H(p~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~x'~) = H(p~x~) + +=> p~x'~ = R^-1^(H(p~x~)) + +== Results + +The algorithm is applied on a few standard images. One of the transformations in shown below: + +*Original Image(left) & Reference Image(right)* + +image::https://github.com/boost-gil/test-images/blob/master/jpeg/suite/matching.jpg[Could not load image.] + +*Histogram matched Image* + +image::https://github.com/boost-gil/test-images/blob/master/jpeg/suite/matching_out.jpg[Could not load image.] + +== Demo + +Usage Syntax: + +[source,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/index.rst b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/index.adoc similarity index 53% rename from doc/image_processing/contrast_enhancement/index.rst rename to doc/modules/ROOT/pages/image_processing/contrast_enhancement/index.adoc index 60c60cd468..536f4f0620 100644 --- a/doc/image_processing/contrast_enhancement/index.rst +++ b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/index.adoc @@ -1,11 +1,4 @@ -Contrast Enhancement -==================== += 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/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc similarity index 61% rename from doc/image_processing/contrast_enhancement/overview.rst rename to doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc index aa7d2a13e7..dd0c4c9ad7 100644 --- a/doc/image_processing/contrast_enhancement/overview.rst +++ b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc @@ -1,15 +1,14 @@ -Overview -======== += 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 +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 are 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 +. xref:image_processing/contrast_enhancement/histogram_equalization.adoc[] +. xref:image_processing/contrast_enhancement/histogram_matching.adoc[] +. `ahe` +. Linear and Non-Linear Contrast stretching diff --git a/doc/modules/ROOT/pages/image_processing/index.adoc b/doc/modules/ROOT/pages/image_processing/index.adoc new file mode 100644 index 0000000000..0332af0b57 --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/index.adoc @@ -0,0 +1,4 @@ += Image Processing + +The GIL documentation sections listed below are dedicated to describe the library +features, structures and algorithms, for image processing and analysis. diff --git a/doc/image_processing/overview.rst b/doc/modules/ROOT/pages/image_processing/overview.adoc similarity index 53% rename from doc/image_processing/overview.rst rename to doc/modules/ROOT/pages/image_processing/overview.adoc index f11b6aa886..2c6b95e035 100644 --- a/doc/image_processing/overview.rst +++ b/doc/modules/ROOT/pages/image_processing/overview.adoc @@ -1,10 +1,9 @@ -Overview -======== += Overview Development of the image processing features was initiated during two Boost projects run in frame of the Google Summer of Code 2019: -- `Image Processing Algorithms `_ -- `Fundamentals of epipolar geometry `_ +* https://github.com/boostorg/gil/projects/5[Image Processing Algorithms] +* https://github.com/boostorg/gil/projects/6[Fundamentals of epipolar geometry] The image processing features were first released with Boost 1.72. diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc new file mode 100644 index 0000000000..edb2264a11 --- /dev/null +++ b/doc/modules/ROOT/pages/index.adoc @@ -0,0 +1,8 @@ += Boost Generic Image Library + +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. + +See xref:examples.adoc[] for the example programs shipped with GIL. diff --git a/doc/modules/ROOT/pages/installation.adoc b/doc/modules/ROOT/pages/installation.adoc new file mode 100644 index 0000000000..b051e849f3 --- /dev/null +++ b/doc/modules/ROOT/pages/installation.adoc @@ -0,0 +1,26 @@ += Installation + +The latest version of Boost.GIL can be downloaded from https://github.com/boostorg/gil. + +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. + +NOTE: The exception to dependencies-free rule of GIL is the I/O extension +which requires client libraries implementing popular image formats +like libpng, libjpeg, etc. + +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++14 support. + +NOTE: Boost.GIL requires C++14 compiler since Boost 1.80. + +For the actual list of currently tested compilers, check results of the library CI +builds linked from the link:https://github.com/boostorg/gil/blob/develop/README.md[README.md] +or inspect the CI services configuration files in the link:https://github.com/boostorg/gil/tree/develop[develop branch] +of the library repository. diff --git a/doc/modules/ROOT/pages/io.adoc b/doc/modules/ROOT/pages/io.adoc new file mode 100644 index 0000000000..4db7c2e5cc --- /dev/null +++ b/doc/modules/ROOT/pages/io.adoc @@ -0,0 +1,705 @@ += IO extensions +:url-bmp-wiki: http://en.wikipedia.org/wiki/BMP_file_format +:url-jpeg-wiki: http://en.wikipedia.org/wiki/JPEG +:url-jpeg-lib: http://www.ijg.org/ +:url-png-wiki: http://en.wikipedia.org/wiki/Portable_Network_Graphics +:url-png-lib: http://libpng.org/pub/png/libpng.html +:url-pnm-wiki: http://en.wikipedia.org/wiki/Portable_anymap +:url-raw-wiki: http://en.wikipedia.org/wiki/Raw_image_format +:url-targa-wiki: http://en.wikipedia.org/wiki/Truevision_TGA +:url-tiff-wiki: http://en.wikipedia.org/wiki/Tagged_Image_File_Format +:url-tiff-lib: http://www.remotesensing.org/libtiff/ +:url-tiff-base-tags: http://www.awaresystems.be/imaging/tiff/tifftags/baseline.html +:url-tiff-extension-tags: http://www.awaresystems.be/imaging/tiff/tifftags/extension.html +:url-bmp-test-files: http://entropymine.com/jason/bmpsuite/ +:url-png-test-files: http://www.schaik.com/pngsuite/pngsuite.html +:url-tiff-libtiff-test-files: http://www.remotesensing.org/libtiff/images.html +:url-tiff-graphicsmagick-test-files: ftp://ftp.graphicsmagick.org/pub/tiff-samples/tiff-sample-images-be.tar.gz + +== Overview + +This extension to boost::gil provides an easy to use interface for reading and +writing various image formats. It also includes a framework for adding +new formats. + +Please see the <> section for +all supported image formats. A basic tutorial is provided in the +<> section. +Also, this extension requires Boost version 1.42 and up. +Furthermore the GIL extension Toolbox is used. + +For adding new image formats please refer to the +<> section. + +== Supported Platforms + +All platforms supported by Boost which have a decent C++ compiler. +Depending on the image format one or more of the following image +libraries might be needed: + +* libtiff +* libjpeg +* libpng +* libraw +* zlib + +The library is designed to support as many formats as required by the user. +For instance, if the user only needs bmp support none of the above mentioned +dependencies are required. + +There are more details available in this documentation on the image format +dependencies. Please see the <> section. + +[#tutorial] +== Tutorial + +Thanks to modern C++ programming techniques the interface for this library +is rather small and easy to use. In this tutorial I'll give you a short +walk-around on how to use this boost::gil extension. +For more details please refer to the <> section. + +For each supported IO format a single top-level header file is provided. +For instance, include `boost/gil/extension/io/tiff.hpp` to be able +to read or write TIFF files. + +=== Reading An Image + +Probably the most common case to read a tiff image can be done as follows: + +[source,cpp] +---- +std::string filename( "image.tif" ); +rgb8_image_t img; +read_image( filename, img, tiff_tag() ); +---- + +The code would be same for all other image formats. The only thing that needs +to change is the tag type ( tiff_tag ) in the read_image call. +The read_image() expects the supplied image type to be compatible with the +image stored in the file. If the user doesn't know what format an image has she +can use read_and_convert_image(). +Another important fact is that read_image() will allocate the appropriate +memory needed for the read operation. There are `read_view` or +`read_and_convert_view` counterparts, if the memory is already allocated. + +Sometimes the user only wants to read a sub-part of an image, +then the above call would look as follows: + +[source,cpp] +---- +read_image( filename + , img + , image_read_settings< tiff_tag >( point_t( 0, 0 ), point_t( 50, 50 ) ) + ); +---- + +The image_read_settings class will provide the user with image format +independent reading setting but can also serve as a pointer for format +dependent settings. +Please see the specific image format sections +<> +for more details. + +=== Writing An Image + +Besides reading the information also writing is the second part of this +Boost.GIL extension. Writing is a lot simpler than reading since an existing +image view contains all the information. + +For instance writing an image can be done as follows: + +[source,cpp] +---- +std::string filename( "image.tif" ); +rgb8_image_t img( 640, 480 ); + +// write data into image + +write_view( filename + , view( img ) + , tiff_tag() + ); +---- + +The interface is similar to reading an image. To add image format specific +parameter the user can use `image_write_info` class. +For instance, a user can specify the JPEG quality when writing like this: + +[source,cpp] +---- +std::string filename( "image.jpg" ); +rgb8_image_t img( 640, 480 ); + +// write data into image + +write_view( filename + , view( img ) + , image_write_info< jpeg_tag >( 95 ) + ); +---- + +The above example will write an image where the jpeg quality is +set to 95 percent. + +=== Reading And Writing In-Memory Buffers + +Reading and writing in-memory buffers are supported as well. See as follows: + +[source,cpp] +---- +// 1. Read an image. +ifstream in( "test.tif", ios::binary ); + +rgb8_image_t img; +read_image( in, img, tiff_tag() ); + +// 2. Write image to in-memory buffer. +stringstream out_buffer( ios_base::out | ios_base::binary ); + +rgb8_image_t src; +write_view( out_buffer, view( src ), tiff_tag() ); + +// 3. Copy in-memory buffer to another. +stringstream in_buffer( ios_base::in | ios_base::binary ); +in_buffer << out_buffer.rdbuf(); + +// 4. Read in-memory buffer to gil image +rgb8_image_t dst; +read_image( in_buffer, dst, tag_t() ); + +// 5. Write out image. +string filename( "out.tif" ); +ofstream out( filename.c_str(), ios_base::binary ); +write_view( out, view( dst ), tiff_tag() ); +---- + +In case the user is using his own stream classes he has to make sure it +has the common interface read, write, seek, close, etc. interface. + +[#using_io] +== Using IO + +=== General Overview + +The tutorial pointed out some use cases for reading and writing images in +various image formats. This section will provide a more thorough overview. + +The next sections will introduce the Read and Write interface. But it might be +worth pointing out that by using some advanced metaprogramming techniques the +interface is rather small and hopefully easy to understand. + +Besides the general interface the user also has the ability to interface +directly with the underlying image format. For that each reader or writer +provides access to the so-called backend. + +For instance: + +[source,cpp] +---- +typedef bmp_tag tag_t; + +typedef get_reader_backend< const std::string + , tag_t + >::type backend_t; + +backend_t backend = read_image_info( bmp_filename + , tag_t() + ); + +BOOST_CHECK_EQUAL( backend._info._width , 127 ); +BOOST_CHECK_EQUAL( backend._info._height, 64 ); +---- + +Of course, the typedef can be removed when using c++11's auto feature. + +=== Read Interface + +As the Tutorial demonstrated there are a few ways to read images. +Here is an enumeration of all read functions with a short description: + +* `read_image` - read into a gil image with no conversion. + Memory is allocated. +* `read_view` - read into a gil view with no conversion. +* `read_and_convert_image` - read and convert into a gil image. + Memory is allocated. +* `read_and_convert_view` - read and convert into a gil view. +* `read_image_info` - read the image header. + +Conversion in this context is necessary if the source (file) has an +incompatible color space with the destination (gil image type). +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` 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 +`read_xxx` function. +The third and last parameter is either an instance of the +`image_read_settings` or just the `FormatTag`. +The settings can be various depending on the format which is being read. +But they all share settings for reading a partial image area. +The first point describes the top left image coordinate whereas the second +are the dimensions in x and y directions. + +Here is an example of setting up partial read: + +[source,cpp] +---- +read_image( filename + , img + , image_read_settings< tiff_tag >( point_t( 0, 0 ), point_t( 50, 50 ) ) + ); +---- + +Each format supports reading just the header information, +using `read_image_info`. Please refer to the format specific sections +under <>. A basic example follows: + +[source,cpp] +---- +image_read_info< tiff_t > info = read_image_info( filename + , tiff_t() + ); +---- + +GIL also comes with a dynamic image extension. +In the context of GIL.IO a user can define an `any_image` type based on +several image types. The IO extension would then pick the matching image type +to the current image file. +The following example shows this feature: + +[source,cpp] +---- +any_image< gray8_image_t + , gray16_image_t + , rgb8_image_t + , rgba8_image_t + > runtime_image; + +read_image( filename + , runtime_image + , tiff_tag() + ); +---- + +During the review it became clear that there is a need to read big images +scanline by scanline. To support such use case a `scanline_reader` is +implemented for all supported image formats. +The `scanline_read_iterators` will then allow to traverse through the image. +The following code sample shows the usage: + +[source,cpp] +---- +typedef tiff_tag tag_t; + +typedef scanline_reader< typename get_read_device< const char* + , tag_t + >::type + , tag_t + > reader_t; + +reader_t reader = make_scanline_reader( "C:/boost/libs/gil/test/extension/io/images/tiff/test.tif", tag_t() ); + +typedef rgba8_image_t image_t; + +image_t dst( reader._info._width, reader._info._height ); +fill_pixels( view(dst), image_t::value_type() ); + +typedef reader_t::iterator_t iterator_t; + +iterator_t it = reader.begin(); +iterator_t end = reader.end(); + +for( int row = 0; it != end; ++it, ++row ) +{ + copy_pixels( interleaved_view( reader._info._width + , 1 + , ( image_t::view_t::x_iterator ) *it + , reader._scanline_length + ) + , subimage_view( view( dst ) + , 0 + , row + , reader._info._width + , 1 + ) + ); +} +---- + +There are many ways to traverse an image but for as of now only by +scanline is supported. + +=== 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` 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 a view object to image being written. +The third and last parameter is either a tag or an +`image_write_info` object containing more settings. +One example for instance is the JPEG quality. +Refer to the format specific sections under <> +to have a list of all the possible settings. + +Writing an any_image<...> is supported. See the following example: + +[source,cpp] +---- +any_image< gray8_image_t + , gray16_image_t + , rgb8_image_t + , rgba8_image_t + > runtime_image; + +// fill any_image + +write_view( filename + , view( runtime_image ) + , tiff_tag() + ); +---- + +=== Compiler Symbols + +The following table gives an overview of all supported compiler symbols +that can be set by the user: + +[cols="1,2"] +|=== +|Symbol |Description + +|`BOOST_GIL_IO_ENABLE_GRAY_ALPHA` +|Enable the color space "gray_alpha". + +|`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. + +|`BOOST_GIL_IO_PNG_1_4_OR_LOWER` +|Allow compiling with libpng 1.4 or lower. + +|`BOOST_GIL_EXTENSION_IO_JPEG_C_LIB_COMPILED_AS_CPLUSPLUS` +|libjpeg is compiled as c++ lib. + +|`BOOST_GIL_EXTENSION_IO_PNG_C_LIB_COMPILED_AS_CPLUSPLUS` +|libpng is compiled as c++ lib. + +|`BOOST_GIL_EXTENSION_IO_TIFF_C_LIB_COMPILED_AS_CPLUSPLUS` +|libtiff is compiled as c++ lib. + +|`BOOST_GIL_EXTENSION_IO_ZLIB_C_LIB_COMPILED_AS_CPLUSPLUS` +|zlib is compiled as c++ lib. + +|`BOOST_GIL_IO_TEST_ALLOW_READING_IMAGES` +|Allow basic test images to be read from local hard drive. The paths can be set in paths.hpp + +|`BOOST_GIL_IO_TEST_ALLOW_WRITING_IMAGES` +|Allow images to be written to the local hard drive. The paths can be set in paths.hpp + +|`BOOST_GIL_IO_USE_BMP_TEST_SUITE_IMAGES` +|Run tests using the bmp test images suite. See _BMP_TEST_FILES + +|`BOOST_GIL_IO_USE_PNG_TEST_SUITE_IMAGES` +|Run tests using the png test images suite. See _PNG_TEST_FILES + +|`BOOST_GIL_IO_USE_PNM_TEST_SUITE_IMAGES` +|Run tests using the pnm test images suite. Send me an email for accessing the files. + +|`BOOST_GIL_IO_USE_TIFF_LIBTIFF_TEST_SUITE_IMAGES` +|Run tests using the targa file format test images suite. See _TIFF_LIB_TIFF_TEST_FILES + +|`BOOST_GIL_IO_USE_TIFF_GRAPHICSMAGICK_TEST_SUITE_IMAGES` +|Run tests using the targa file format test images suite. See _TIFF_GRAPHICSMAGICK_TEST_FILES +|=== + +[#supported_image_formats] +=== Supported Image Formats + +==== BMP + +For a general overview of the BMP image file format go to the +following link:{url-bmp-wiki}[BMP_Wiki]. + +Please note, the code has not been tested on X Windows System variations +of the BMP format which are usually referred to as XBM and XPM formats. + +Here, only the MS Windows and OS/2 format is relevant. + +Currently the code is able to read and write the following image types: + +Read:: `gray1_image_t`, `gray4_image_t`, `gray8_image_t`, `rgb8_image_t` and `rgba8_image_t` +Write:: `rgb8_image_t` and `rgba8_image_t` + +The lack of having an indexed image type in gil restricts the current +interface to only write out non-indexed images. +This is subject to change soon. + +==== JPEG + +For a general overview of the JPEG image file format go to the +following link:{url-jpeg-wiki}[JPEG_Wiki]. + +This jpeg extension is based on the libjpeg library which can be +found here, link:{url-jpeg-lib}[JPEG_Lib]. + +All versions starting from 8x are supported. + +The user has to make sure this library is properly installed. +I strongly recommend the user to build the library yourself. +It could potentially save you a lot of trouble. + +Currently the code is able to read and write the following image types: + +Read:: `gray8_image_t`, `rgb8_image_t`, `cmyk8_image_t` +Write:: `gray8_image_t`, `rgb8_image_t`, `cmyk8_image_t` + +Reading YCbCr or YCCK images is possible but might result in inaccuracies since +both color spaces aren't available yet for gil. +For now these color spaces are read as rgb images. +This is subject to change soon. + +==== PNG + +For a general overview of the PNG image file format go to the +following link:{url-png-wiki}[PNG_Wiki]. + +This png extension is based on the libpng, which can be found +here, link:{url-png-lib}[PNG_Lib]. + +All versions starting from 1.5.x are supported. + +The user has to make sure this library is properly installed. +I strongly recommend the user to build the library yourself. +It could potentially save you a lot of trouble. + +Currently the code is able to read and write the following image types: + +Read:: gray1, gray2, gray4, gray8, gray16, gray_alpha_8, gray_alpha_16, rgb8, rgb16, rgba8, rgba16 +Write:: gray1, gray2, gray4, gray8, gray16, gray_alpha_8, gray_alpha_16, rgb8, rgb16, rgba8, rgba16 + +For reading gray_alpha images the user has to compile application with `BOOST_GIL_IO_ENABLE_GRAY_ALPHA` +macro defined. This color space is defined in the toolbox by using `gray_alpha.hpp`. + +==== PNM + +For a general overview of the PNM image file format go to the +following link:{url-pnm-wiki}[PNM_Wiki]. No external library is needed for the pnm format. + +The extension can read images in both flavours of the formats, ASCII and binary, +that is types from P1 through P6; can write only binary formats. + +Currently the code is able to read and write the following image types: + +Read:: gray1, gray8, rgb8 +Write:: gray1, gray8, rgb8 + +When reading a mono text image the data is read as a gray8 image. + +==== RAW + +For a general overview see link:{url-raw-wiki}[RAW_Wiki]. + +Currently the extension is only able to read rgb8 images. + +==== TARGA + +For a general overview of the BMP image file format go to the +following link:{url-targa-wiki}[TARGA_Wiki]. + +Currently the code is able to read and write the following image types: + +Read:: rgb8_image_t and rgba8_image_t +Write:: rgb8_image_t and rgba8_image_t + +The lack of having an indexed image type in gil restricts the current +interface to only write out non-indexed images. +This is subject to change soon. + +==== TIFF + +For a general overview of the TIFF image file format go to the +following link:{url-tiff-wiki}[TIFF_Wiki]. + +This tiff extension is based on the libtiff, which can be found, link:{url-tiff-lib}[TIFF_Lib]. + +All versions starting from 3.9.x are supported. + +The user has to make sure this library is properly installed. I strongly +recommend the user to build the library yourself. It could potentially +save you a lot of trouble. + +TIFF images can virtually encode all kinds of channel sizes representing +various color spaces. Even planar images are possible. +For instance, `rbg323` or `gray7`. The channels also can have specific +formats, like integer values or floating point values. + +For a complete set of options please consult the following websites: + +* link:{url-tiff-base-tags}[TIFF_Base_Tags] +* link:{url-tiff-extension-tags}[TIFF_Extension_Tags] + +The author of this extension is not claiming all tiff formats are supported. +This extension is likely to be a moving target adding new features with each +new milestone. Here is an incomplete list: + +* Multi-page TIFF - read only +* Strip TIFF - read and write support +* Tiled TIFF - read and write support with user defined tiled sizes +* Bit images TIFF - fully supported, like `gray1_image_t` (minisblack) +* Planar TIFF - fully supported +* Floating-point TIFF - fully supported +* Palette TIFF - supported but no indexed image type is available as of now + +This gil extension uses two different test image suites to test read and +write capabilities. See `test_image` folder. +It's advisable to use ImageMagick test viewer to display images. + +[#extending_gil_io_with_new_formats] +=== Extending GIL::IO with new Formats + +Extending the gil::io with new formats is meant to be simple and +straightforward. Before adding I would recommend to have a look at existing +implementations and then trying to follow a couple of guidelines: + +* Create the following files for your new xxx format + ** `xxx_read.hpp` - only includes read code + ** `xxx_write.hpp` - only includes write code + ** `xxx_all.hpp` - includes xxx_read.hpp and xxx_write.hpp +* Add the code to the `boost::gil::detail` namespace +* Create a tag type for the new format. Like this: ++ +[source,cpp] +---- +struct xxx_tag : format_tag {}; +---- + +* Create the image_read_info for the new format. It contains all the + information that is necessary to read an image. It should be filled + and returned by the `get_info` member of the reader class. See below: ++ +[source,cpp] +---- +template<> struct image_read_info< xxx_tag > {}; +---- + +* Create the image_write_info for the new format. It contains all the + information that is necessary to write an image: ++ +[source,cpp] +---- +template<> struct image_write_info< xxx_tag > {}; +---- + +* Use the following reader skeleton as a start: ++ +[source,cpp] +---- +template< typename Device + , typename ConversionPolicy + > +class reader< Device + , xxx_tag + , ConversionPolicy + > + : public reader_base< xxx_tag + , ConversionPolicy + > +{ +private: + + typedef typename ConversionPolicy::color_converter_type cc_t; + +public: + + reader( Device& device ) + : _io_dev( device ) + {} + + reader( Device& device + , const cc_t& cc + ) + : _io_dev( device ) + , reader_base< xxx_tag + , ConversionPolicy + >( cc ) + {} + + image_read_info< xxx_tag > get_info() + { + // your implementation here + } + + template< typename View > + void apply( const View& dst_view ) + { + // your implementation here + } +}; +---- + +* The writer skeleton: ++ +[source,cpp] +---- +template< typename Device > +class writer< Device + , xxx_tag + > +{ +public: + + writer( Device & file ) + : out(file) + {} + + template + void apply( const View& view ) + { + // your implementation here + } + + template + void apply( const View& view + , const image_write_info< xxx_tag >& info ) + { + // your implementation here + } +}; +---- + +== Running gil::io tests + +gil::io comes with a large suite of test cases which reads and writes various +file formats. It uses some test image suites which can be found online or +which can be demanded from me by sending me an email. + +There are some test images created by me in the test folder. +To enable unit tests which make use of them set the following compiler options +`BOOST_GIL_IO_TEST_ALLOW_READING_IMAGES` and +`BOOST_GIL_IO_TEST_ALLOW_WRITING_IMAGES`. + +The following list provides all links to the image suites the compiler symbol +to enable the tests: + +BMP:: link:{url-bmp-test-files}[BMP_TEST_FILES] -- `BOOST_GIL_IO_USE_BMP_TEST_SUITE_IMAGES` +PNG:: link:{url-png-test-files}[PNG_TEST_FILES] -- `BOOST_GIL_IO_USE_PNG_TEST_SUITE_IMAGES` +PNM:: request files from me -- `BOOST_GIL_IO_USE_PNM_TEST_SUITE_IMAGES` +TIFF:: link:{url-tiff-libtiff-test-files}[TIFF_LIB_TIFF_TEST_FILES] -- `BOOST_GIL_IO_USE_TIFF_LIBTIFF_TEST_SUITE_IMAGES` +TIFF:: link:{url-tiff-graphicsmagick-test-files}[TIFF_GRAPHICSMAGICK_TEST_FILES] -- `BOOST_GIL_IO_USE_TIFF_GRAPHICSMAGICK_TEST_SUITE_IMAGES` diff --git a/doc/modules/ROOT/pages/naming.adoc b/doc/modules/ROOT/pages/naming.adoc new file mode 100644 index 0000000000..06625910f5 --- /dev/null +++ b/doc/modules/ROOT/pages/naming.adoc @@ -0,0 +1,50 @@ += Naming Conventions + +Description of established naming conventions used in source code of GIL, +tests and examples. + +== Concrete Types + +Concrete (non-generic) GIL types follow this naming convention: + +[source,cpp] +---- +ColorSpace + BitDepth + [f | s]+ [c] + [_planar] + [_step] + ClassType + _t +---- + +where: + +- `ColorSpace` indicates layout and ordering of components. + For example, `rgb`, `bgr`, `cmyk`, `rgba`. + +- `BitDepth` indicates the bit depth of the color channel. + For example, `8`, `16`, `32`. + +- By default, type of channel is unsigned integral. + The `s` tag indicates signed integral. + The `f` tag indicates a floating point type, which is always signed. + +- By default, objects operate on mutable pixels. + The `c` tag indicates object operating over immutable pixels. + +- `_planar` indicates planar organization (as opposed to interleaved). + +- `_step` indicates special image views, locators and iterators which + traverse the data in a non-trivial way. For example, backwards or every other + pixel. + +- `ClassType` is `_image` (image), `_view` (image view), `_loc` (pixel + 2D locator) `_ptr` (pixel iterator), `_ref` (pixel reference), + `_pixel` (pixel value). + +- `_t` suffix indicates it is a name of a type. + +For example: + +[source,cpp] +---- +bgr8_image_t a; // 8-bit interleaved BGR image +cmyk16_pixel_t b; // 16-bit CMYK pixel value; +cmyk16c_planar_ref_t c(b); // const reference to a 16-bit planar CMYK pixel. +rgb32f_planar_step_ptr_t d; // step pointer to a 32-bit planar RGB pixel. +---- diff --git a/doc/modules/ROOT/pages/numeric.adoc b/doc/modules/ROOT/pages/numeric.adoc new file mode 100644 index 0000000000..3e11ffea68 --- /dev/null +++ b/doc/modules/ROOT/pages/numeric.adoc @@ -0,0 +1,5 @@ += Numeric extension + +== Overview + +_TODO_ diff --git a/doc/modules/ROOT/pages/reference.adoc b/doc/modules/ROOT/pages/reference.adoc new file mode 100644 index 0000000000..f98dcb75b4 --- /dev/null +++ b/doc/modules/ROOT/pages/reference.adoc @@ -0,0 +1,25 @@ +// +// Copyright (c) 2026 Samuel Debionne +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/gil +// + +[#reference] += Reference + +The complete API reference is generated from the header comments with +Doxygen and published alongside this documentation. + +link:reference/index.html[Open the API Reference,window=_blank] + +NOTE: This library's API reference is planned to move to a +https://github.com/cppalliance/mrdocs[MrDocs]-generated, AsciiDoc-native +reference (as https://www.boost.org/doc/libs/master/libs/url/doc/html/url.html[Boost.URL] +and https://www.boost.org/doc/libs/master/libs/redis/doc/html/redis.html[Boost.Redis] +already do), so that API pages can be cross-referenced directly from the rest +of this documentation. That migration is blocked on a MrDocs issue with +GIL's CRTP-based pixel/image/iterator/locator types; see `doc/mrdocs.yml` +for details. diff --git a/doc/toolbox.rst b/doc/modules/ROOT/pages/toolbox.adoc similarity index 52% rename from doc/toolbox.rst rename to doc/modules/ROOT/pages/toolbox.adoc index 86a5d11335..87b3696fda 100644 --- a/doc/toolbox.rst +++ b/doc/modules/ROOT/pages/toolbox.adoc @@ -1,10 +1,6 @@ -ToolBox extension -================= += ToolBox extension -.. _Reference: reference.html - -Overview --------- +== Overview ToolBox provides collection of Boost.GIL extensions which are too small to be maintained as standalone extensions. @@ -16,29 +12,32 @@ Content: * Color spaces: CMYKA, Gray with Alpha, HSL, HSV, Lab, XYZ * Metafunctions: - - * ``channel_type`` - * ``channel_type_to_index`` - * ``get_num_bits`` - * ``get_pixel_type`` - * ``is_bit_aligned`` - * ``is_homogeneous`` - * ``is_similar`` - * ``pixel_bit_size`` ++ +-- +* `channel_type` +* `channel_type_to_index` +* `get_num_bits` +* `get_pixel_type` +* `is_bit_aligned` +* `is_homogeneous` +* `is_similar` +* `pixel_bit_size` +-- * Image types: - - * ``indexed_image`` ++ +-- +* `indexed_image` +-- This extension will hopefully be added on by the community. Since the extension is header-only, user just needs to include -its main header ``#include ``. +its main header `#include `. -All definitions of the toolbox belong to the ``boost::gil`` namespace. +All definitions of the toolbox belong to the `boost::gil` namespace. -Folder Structure ----------------- +== Folder Structure The toolbox structured in the following sub-directories: @@ -47,13 +46,11 @@ The toolbox structured in the following sub-directories: * metafunctions * image_types -Acknowledgements ----------------- +== Acknowledgements Thanks to all the people who have reviewed this library and made suggestions for improvements. -Reference ---------- +== Reference -The Reference_ section. +The xref:reference.adoc[Reference] section. diff --git a/doc/modules/ROOT/pages/tutorial/gradient.adoc b/doc/modules/ROOT/pages/tutorial/gradient.adoc new file mode 100644 index 0000000000..aa1cc6769b --- /dev/null +++ b/doc/modules/ROOT/pages/tutorial/gradient.adoc @@ -0,0 +1,1019 @@ += Tutorial: Image Gradient + +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 +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: + +[source,cpp] +---- +D[x] = (I[x-1] - I[x+1]) / 2 +---- + +For simplicity, we will also ignore the boundary cases - the pixels along the +edges of the image for which one of the neighbors is not defined. The focus of +this document is how to use GIL, not how to create a good gradient generation +algorithm. + +== Interface and Glue Code + +Let us first start with 8-bit unsigned grayscale image as the input and 8-bit +signed grayscale image as the output. + +Here is how the interface to our algorithm looks like: + +[source,cpp] +---- +#include + +using namespace boost::gil; + +void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + assert(src.dimensions() == dst.dimensions()); + ... // compute the gradient +} +---- + +`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 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 +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. +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 +their constness to their pixels - a constant reference to an image will not +allow for modifying its pixels. + +Most GIL algorithms operate on image views; images are rarely +needed. GIL's design is very similar to that of the STL. The STL +equivalent of GIL's image is a container, like `std::vector`, +whereas GIL's image view corresponds to STL range, which is often +represented with a pair of iterators. STL algorithms operate on +ranges, just like GIL algorithms operate on image views. + +GIL's image views can be constructed from raw data - the dimensions, +the number of bytes per row and the pixels, which for chunky views are +represented with one pointer. Here is how to provide the glue between +your code and GIL: + +[source,cpp] +---- +void ComputeXGradientGray8( + unsigned char const* src_pixels, std::ptrdiff_t src_row_bytes, + int w, int h, + signed char* dst_pixels, std::ptrdiff_t 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); +} +---- + +This glue code is very fast and views are lightweight - in the above example +the views have a size of 16 bytes. They consist of a pointer to the top left +pixel and three integers - the width, height, and number of bytes per row. + +== First Implementation + +Focusing on simplicity at the expense of speed, we can compute the horizontal +gradient like this: + +[source,cpp] +---- +void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + for (int y = 0; y < src.height(); ++y) + for (int x = 1; x < src.width() - 1; ++x) + dst(x, y) = (src(x-1, y) - src(x+1, y)) / 2; +} +---- + +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. +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.) + +While the above code is easy to read, it is not very fast, because the binary +`operator()` computes the location of the pixel in a 2D grid, which involves +addition and multiplication. Here is a faster version of the above: + +[source,cpp] +---- +void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + 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); + + for (int x = 1; x < src.width() - 1; ++x) + dst_it[x] = (src_it[x-1] - src_it[x+1]) / 2; + } +} +---- + +We use pixel iterators initialized at the beginning of each row. GIL's +iterators are Random Access Traversal iterators. If you are not +familiar with random access iterators, think of them as if they were +pointers. In fact, in the above example the two iterator types are raw +C pointers and their `operator[]` is a fast pointer indexing +operator. + +The code to compute gradient in the vertical direction is very +similar: + +[source,cpp] +---- +void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + for (int x = 0; x < src.width(); ++x) + { + gray8c_view_t::y_iterator src_it = src.col_begin(x); + gray8s_view_t::y_iterator dst_it = dst.col_begin(x); + + for (int y = 1; y < src.height() - 1; ++y) + dst_it[y] = (src_it[y-1] - src_it[y+1]) / 2; + } +} +---- + +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 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; +traversing an image vertically results in lots of cache misses. A much more +efficient and cache-friendly version will iterate over the columns in the inner +loop: + +[source,cpp] +---- +void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + for (int y = 1; y < src.height() - 1; ++y) + { + gray8c_view_t::x_iterator src1_it = src.row_begin(y-1); + gray8c_view_t::x_iterator src2_it = src.row_begin(y+1); + gray8s_view_t::x_iterator dst_it = dst.row_begin(y); + + for (int x = 0; x < src.width(); ++x) + { + *dst_it = ((*src1_it) - (*src2_it)) / 2; + ++dst_it; + ++src1_it; + ++src2_it; + } + } +} +---- + +This sample code also shows an alternative way of using pixel iterators - +instead of `operator[]` one could use increments and dereferences. + +== Using Locators + +Unfortunately this cache-friendly version requires the extra hassle of +maintaining two separate iterators in the source view. For every pixel, we +want to access its neighbors above and below it. Such relative access can be +done with GIL locators: + +[source,cpp] +---- +void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + 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); + + for (int x = 0; x < src.width(); ++x) + { + *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_t(-src.width(), 1); // carriage return + } +} +---- + +The first line creates a locator pointing to the first pixel of the +second row of the source view. A GIL pixel locator is very similar to +an iterator, except that it can move both horizontally and +vertically. `src_loc.x()` and `src_loc.y()` return references to a +horizontal and a vertical iterator respectively, which can be used to +move the locator along the desired dimension, as shown +above. Additionally, the locator can be advanced in both dimensions +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 - 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 +computations than necessary. The code `src_loc(0,1)` has to compute +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: + +[source,cpp] +---- +void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + gray8c_view_t::xy_locator src_loc = src.xy_at(0,1); + gray8c_view_t::xy_locator::cached_location_t above = src_loc.cache_location(0,-1); + gray8c_view_t::xy_locator::cached_location_t below = src_loc.cache_location(0, 1); + + for (int y = 1; y < src.height() - 1; ++y) + { + 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.x(); + } + src_loc += point_t(-src.width(), 1); + } +} +---- + +In this example `src_loc[above]` corresponds to a fast pointer indexing +operation and the code is efficient. + +== Creating a Generic Version of GIL Algorithms + +Let us make our `x_gradient` more generic. It should work with any image +views, as long as they have the same number of channels. The gradient +operation is to be computed for each channel independently. + +Here is how the new interface looks like: + +[source,cpp] +---- +template +void x_gradient(SrcView const& src, DstView const& dst) +{ + gil_function_requires>(); + gil_function_requires>(); + gil_function_requires + < + ColorSpacesCompatibleConcept + < + typename color_space_type::type, + typename color_space_type::type + > + >(); + + ... // compute the gradient +} +---- + +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 +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 +(i.e. have the same set of channels). + +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 +set of requirements; in other words, they have to \emph model the +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 +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 +`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 +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 +using concept checks is the sometimes severe impact they have on +compile time. This is why GIL performs concept checks only in debug +mode, and only if `BOOST_GIL_USE_CONCEPT_CHECK` is defined (off by +default). + +The body of the generic function is very similar to that of the +concrete one. The biggest difference is that we need to loop over the +channels of the pixel and compute the gradient for each channel: + +[source,cpp] +---- +template +void x_gradient(SrcView const& src, DstView const& dst) +{ + 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 (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; + } +} +---- + +Having an explicit loop for each channel could be a performance problem. +GIL allows us to abstract out such per-channel operations: + +[source,cpp] +---- +template +struct halfdiff_cast_channels +{ + template + auto operator()(T const& in1, T const& in2) const -> Out + { + return Out((in1 - in2) / 2); + } +}; + +template +void x_gradient(SrcView const& src, DstView const& dst) +{ + using dst_channel_t = typename channel_type::type; + + 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) + { + static_transform(src_it[x-1], src_it[x+1], dst_it[x], + halfdiff_cast_channels()); + } + } +} +---- + +The `static_transform` is an example of a channel-level GIL algorithm. +Other such algorithms are `static_generate`, `static_fill` and +`static_for_each`. They are the channel-level equivalents of STL +`generate`, `transform`, `fill` and `for_each` respectively. +GIL channel algorithms use static recursion to unroll the loops; they never +loop over the channels explicitly. + +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: + +[source,cpp] +---- +// Calling with 16-bit grayscale data +void XGradientGray16_Gray32( + unsigned short const* src_pixels, std::ptrdiff_t src_row_bytes, + int w, int h, + signed int* dst_pixels, std::ptrdiff_t 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, std::ptrdiff_t src_row_bytes, + int w, int h, + signed short* dst_pixels, std::ptrdiff_t 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, + 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, reinterpret_cast(dst_pixels), dst_row_bytes); + x_gradient(src,dst); +} +---- + +As these examples illustrate, both the source and the destination can be +interleaved or planar, of any channel depth (assuming the destination channel +is assignable to the source), and of any compatible color spaces. + +GIL 2.1 can also natively represent images whose channels are not +byte-aligned, such as 6-bit RGB222 image or a 1-bit Gray1 image. +GIL algorithms apply to these images natively. See the design guide or sample +files for more on using such images. + +== Image View Transformations + +One way to compute the y-gradient is to rotate the image by 90 degrees, +compute the x-gradient and rotate the result back. +Here is how to do this in GIL: + +[source,cpp] +---- +template +void y_gradient(SrcView const& src, DstView const& dst) +{ + x_gradient(rotated90ccw_view(src), rotated90ccw_view(dst)); +} +---- + +`rotated90ccw_view` takes an image view and returns an image view +representing 90-degrees counter-clockwise rotation of its input. It is +an example of a GIL view transformation function. GIL provides a +variety of transformation functions that can perform any axis-aligned +rotation, transpose the view, flip it vertically or horizontally, +extract a rectangular subimage, perform color conversion, subsample +view, etc. The view transformation functions are fast and shallow - +they don't copy the pixels, they just change the "coordinate system" +of accessing the pixels. `rotated90cw_view`, for example, returns a +view whose horizontal iterators are the vertical iterators of the +original view. The above code to compute `y_gradient` is slow +because of the memory access pattern; using `rotated90cw_view` does +not make it any slower. + +Another example: suppose we want to compute the gradient of the N-th +channel of a color image. Here is how to do that: + +[source,cpp] +---- +template +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 +a step view - a view whose horizontal iterator skips over two channels +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 +pixels in the view, use: + +[source,cpp] +---- +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 +represented as a single subsampled view whose step is the product of +the steps of the two views. + +== 1D pixel iterators + +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 +nice and instructional to see how we can rewrite it in canonical +form. The way to do that in GIL is to write a version that works for +every pixel, but apply it only on the subimage that excludes the first +and last column: + +[source,cpp] +---- +void x_gradient_unguarded(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + 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); + + for (int x = 0; x < src.width(); ++x) + dst_it[x] = (src_it[x-1] - src_it[x+1]) / 2; + } +} + +void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + 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())); +} +---- + +`subimage_view` is another example of a GIL view transformation +function. It takes a source view and a rectangular region (in this +case, defined as x_min,y_min,width,height) and returns a view +operating on that region of the source view. The above implementation +has no measurable performance degradation from the version that +operates on the original views. + +Now that `x_gradient_unguarded` operates on every pixel, we can +rewrite it more compactly: + +[source,cpp] +---- +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) + *dst_it = (src_it.x()[-1] - src_it.x()[1]) / 2; +} +---- + +GIL image views provide `begin()` and `end()` methods that return +one dimensional pixel iterators which iterate over each pixel in the +view, left to right and top to bottom. They do a proper "carriage +return" - they skip any unused bytes at the end of a row. As such, +they are slightly suboptimal, because they need to keep track of their +current position with respect to the end of the row. Their increment +operator performs one extra check (are we at the end of the row?), a +check that is avoided if two nested loops are used instead. These +iterators have a method `x()` which returns the more lightweight +horizontal iterator that we used previously. Horizontal iterators have +no notion of the end of rows. In this case, the horizontal iterators +are raw C pointers. In our example, we must use the horizontal +iterators to access the two neighbors properly, since they could +reside outside the image view. + +== STL Equivalent Algorithms + +GIL provides STL equivalents of many algorithms. For example, +`std::transform` is an STL algorithm that sets each element in a +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 +abstract that operation in a function object, we can use GIL's +`transform_pixel_positions` to do that: + +[source,cpp] +---- +struct half_x_difference +{ + auto operator()(gray8c_loc_t const& src_loc) const -> int + { + return (src_loc.x()[-1] - src_loc.x()[1]) / 2; + } +}; + +void x_gradient_unguarded(gray8c_view_t const& src, gray8s_view_t const& dst) +{ + transform_pixel_positions(src, dst, half_x_difference()); +} +---- + +GIL provides the algorithms `for_each_pixel` and +`transform_pixels` which are image view equivalents of STL +`std::for_each` and `std::transform`. It also provides +`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 +through the pixels using the more efficient two nested loops (as +opposed to the single loop using 1-D iterators). + +== Color Conversion + +Instead of computing the gradient of each color plane of an image, we +often want to compute the gradient of the luminosity. In other words, +we want to convert the color image to grayscale and compute the +gradient of the result. Here is how to compute the luminosity gradient of +a 32-bit float RGB image: + +[source,cpp] +---- +void x_gradient_rgb_luminosity(rgb32fc_view_t const& src, gray8s_view_t const& dst) +{ + x_gradient(color_converted_view(src), dst); +} +---- + +`color_converted_view` is a GIL view transformation function that +takes any image view and returns a view in a target color space and +channel depth (specified as template parameters). In our example, it +constructs an 8-bit integer grayscale view over 32-bit float RGB +pixels. Like all other view transformation functions, +`color_converted_view` is very fast and shallow. It doesn't copy the +data or perform any color conversion. Instead it returns a view that +performs color conversion every time its pixels are accessed. + +In the generic version of this algorithm we might like to convert the +color space to grayscale, but keep the channel depth the same. We do +that by constructing the type of a GIL grayscale pixel with the same +channel as the source, and color convert to that pixel type: + +[source,cpp] +---- +template +void x_luminosity_gradient(SrcView const& src, DstView const& dst) +{ + using gray_pixel_t = pixel::type, gray_layout_t>; + x_gradient(color_converted_view(src), dst); +} +---- + +When the destination color space and channel type happens to be the +same as the source one, color conversion is unnecessary. GIL detects +this case and avoids calling the color conversion code at all - +i.e. `color_converted_view` returns back the source view unchanged. + +== 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 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: + +[source,cpp] +---- +void x_luminosity_gradient(rgb32fc_view_t const& src, gray8s_view_t const& dst) +{ + gray8_image_t ccv_image(src.dimensions()); + copy_pixels(color_converted_view(src), view(ccv_image)); + + x_gradient(const_view(ccv_image), dst); +} +---- + +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 +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. + +Creating a generic version of the above is a bit trickier: + +[source,cpp] +---- +template +void x_luminosity_gradient(SrcView const& src, DstView const& dst) +{ + using d_channel_t = typename channel_type::type; + using channel_t = typename detail::channel_convert_to_unsigned::type; + using gray_pixel_t = pixel; + using gray_image_t = image; + + gray_image_t ccv_image(src.dimensions()); + copy_pixels(color_converted_view(src), view(ccv_image)); + x_gradient(const_view(ccv_image), dst); +} +---- + +First we use the `channel_type` metafunction to get the channel type +of the destination view. A metafunction is a function operating on +types. In GIL metafunctions are class templates (declared with +`struct` type specifier) which take their parameters as template +parameters and return their result in a nested typedef called +`type`. In this case, `channel_type` is a unary metafunction which +in this example is called with the type of an image view and returns +the type of the channel associated with that image view. + +GIL constructs that have an associated pixel type, such as pixels, +pixel iterators, locators, views and images, all model +`PixelBasedConcept`, which means that they provide a set of +metafunctions to query the pixel properties, such as `channel_type`, +`color_space_type`, `channel_mapping_type`, and `num_channels`. + +After we get the channel type of the destination view, we use another +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 +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: + +[source,cpp] +---- +template +void x_luminosity_gradient(SrcView const& src, DstView const& dst) +{ + 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)); + x_gradient(const_view(ccv_image), dst); +} +---- + +GIL provides a set of metafunctions that generate GIL types - +`image_type` is one such meta-function that constructs the type of +an image from a given channel type, color layout, and +planar/interleaved option (the default is interleaved). There are also +similar meta-functions to construct the types of pixel references, +iterators, locators and image views. GIL also has metafunctions +`derived_pixel_reference_type`, `derived_iterator_type`, +`derived_view_type` and `derived_image_type` that construct the +type of a GIL construct from a given source one by changing one or +more properties of the type and keeping the rest. + +From the image type we can use the nested typedef `value_type` to +obtain the type of a pixel. GIL images, image views and locators have +nested typedefs `value_type` and `reference` to obtain the type of +the pixel and a reference to the pixel. If you have a pixel iterator, +you can get these types from its `iterator_traits`. Note also the +algorithm `copy_and_convert_pixels`, which is an abbreviated version +of `copy_pixels` with a color converted source view. + +== 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 +object that computes the value of the Mandelbrot set at a given +location (x,y) in the image: + +[source,cpp] +---- +// models PixelDereferenceAdaptorConcept +template // Models PixelValueConcept +struct mandelbrot_fn +{ + 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() = 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) {} + + auto operator()(point_t const& p) const -> result_type + { + // normalize the coords to (-2..1, -1.5..1.5) + // (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; + + auto get_num_iter(point const& p) const -> double + { + point Z(0,0); + 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/static_cast(MAX_ITER); + } + return 0; + } +}; +---- + +We can now use GIL's `virtual_2d_locator` with this function object +to construct a rgb8 Mandelbrot view of size 200x200 pixels: + +[source,cpp] +---- +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), + 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_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: + +[source,cpp] +---- +#include + +gray8s_image_t img(dims); +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) +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: + +image::mandel.jpg[] + +== 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, +however, the properties of an image view, such as its color space and +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 +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 +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. + +Let's 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. + +First, we need to make a function object that contains the templated +destination view and has an application operator taking a templated +source view: + +[source,cpp] +---- +#include + +template +struct x_gradient_obj +{ + using result_type = void; // required typedef + + DstView const& _dst; + x_gradient_obj(DstView const& dst) : _dst(dst) {} + + template + 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 an image view variant and calls `variant2::visit` passing it the +function object: + +[source,cpp] +---- +template +void x_luminosity_gradient(any_image_view const& src, DstView const& 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 +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 +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 +statement. Algorithms that perform an operation for each pixel in an +image view have practically no performance degradation when used with +a variant. + +Here is how we can construct a variant and invoke the algorithm: + +[source,cpp] +---- +#include + +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)); +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 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 `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 +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)` +returns an `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 +instantiated with every possible combination of the two input types! +This can take a toll on both the compile time and the executable size. + +== Conclusion + +This tutorial provides a glimpse at the challenges associated with +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 +the algorithm can work with fully abstracted virtual images, and even +images whose type is specified at run time. The associated video +presentation also demonstrates that even for complex scenarios the +generated assembly is comparable to that of a C version of the +algorithm, hand-written for the specific image types. + +Yet, even for such a simple algorithm, we are far from making a fully +generic and optimized code. In particular, the presented algorithms +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 +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 +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, +`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 +well. Channel-level operations, such as the half-difference, could be +abstracted out into atomic channel-level algorithms and performance +overloads could be provided for concrete channel +types. Processor-specific operations could be used, for example, to +perform the operation over an entire row of pixels simultaneously, or +the data could be pre-fetched. All of these optimizations can be +realized as performance specializations of the generic +algorithm. Finally, compilers, while getting better over time, are +still failing to fully optimize generic code in some cases, such as +failing to inline some functions or put some variables into +registers. If performance is an issue, it might be worth trying your +code with different compilers. diff --git a/doc/modules/ROOT/pages/tutorial/histogram.adoc b/doc/modules/ROOT/pages/tutorial/histogram.adoc new file mode 100644 index 0000000000..a8a3b22f3e --- /dev/null +++ b/doc/modules/ROOT/pages/tutorial/histogram.adoc @@ -0,0 +1,100 @@ += Tutorial: Histogram + +This is a short tutorial presenting an example of a very simple sample of code +from an existing code base that calculates the histogram of an image. +Next, the program is rewritten using GIL features. + +== Original implementation + +Actual code from a commercial software product that computes the luminosity +histogram (variable names have been changed and unrelated parts removed): + +[source,cpp] +---- +void luminosity_hist( + std::uint8_t const* r, std::uint8_t const* g, std::uint8_t const* b, + int rows, int cols, int sRowBytes, Histogram* hist) +{ + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + int v = RGBToGray(r[c], g[c], b[c]); // call internal function or macro + (*hist)[v]++; + } + r += sRowBytes; + g += sRowBytes; + b += sRowBytes; + } +} +---- + +Let's consider the following issues of the implementation above: + +* Works only for RGB (duplicate versions exist for other color spaces) +* Works only for 8-bit images (duplicate versions exist) +* Works only for planar images + +== GIL implementation + +[source,cpp] +---- +template +void grayimage_histogram(GrayView& img, R& hist) +{ + for (typename GrayView::iterator it=img.begin(); it!=img.end(); ++it) + ++hist[*it]; +} + +template +void luminosity8bit_hist(View& img, R& hist) +{ + grayimage_histogram(color_converted_view(img),hist); +} +---- + +Using the Boost.Lambda library (or C++11 lambda) features it can be written +even simpler: + +[source,cpp] +---- +using boost::lambda; + +template +void grayimage_histogram(GrayView& img, R& hist) +{ + for_each_pixel(img, ++var(hist)[_1]); +} +---- + +Let's consider the following advantages of the GIL version: + +* Works with any supported channel depth, color space, channel ordering + (RGB vs BGR), and row alignment policy. +* Works for both planar and interleaved images. +* Works with new color spaces, channel depths and image types that can be + provided in future extensions of GIL +* The second version is as efficient as the hand-coded version + +Shortly, it is also very flexible. + +For example, to compute the histogram of the second channel of the top left +quadrant of the image, taking every other row and column, call: + +[source,cpp] +---- +grayimage_histogram( + nth_channel_view( + subsampled_view( + subimage_view(img, + 0,0, img.width() / 2, img.height() / 2), // upper left quadrant + 2, 2 // skip every other row and column + ), + 1 // index of the second channel (for example, green for RGB) + ), + hist +); +---- + +Since GIL operates on the source pixels of `img` object directly, no extra +memory is allocated and no images are copied. diff --git a/doc/modules/ROOT/pages/tutorial/video.adoc b/doc/modules/ROOT/pages/tutorial/video.adoc new file mode 100644 index 0000000000..1fc558009a --- /dev/null +++ b/doc/modules/ROOT/pages/tutorial/video.adoc @@ -0,0 +1,11 @@ += Tutorial: Video Lecture + +This is original video lecture about GIL presented by Lubomir Bourdev. + +++++ +

+ +
+++++ + +Source link: https://www.youtube.com/watch?v=sR8Wjg0pceE diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml new file mode 100644 index 0000000000..df05bd2538 --- /dev/null +++ b/doc/mrdocs.yml @@ -0,0 +1,50 @@ +# +# Copyright (c) 2026 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) +# +# Official repository: https://github.com/boostorg/gil +# +# NOT YET WIRED IN. This is a best-effort MrDocs config prepared for when +# https://github.com/cppalliance/mrdocs handles GIL's headers without crashing. +# As of MrDocs 0.8.0, most of GIL's core headers (anything using the CRTP +# proxy-reference pattern: pixel, image, image_view, locator, the pixel +# iterators, algorithm.hpp, rgb.hpp, and all of image_processing/) segfault +# the tool in isolation -- this is not a config problem. Once that's fixed +# upstream, add to antora.yml: +# ext: +# cpp-reference: +# config: doc/mrdocs.yml +# and replace modules/ROOT/pages/reference.adoc's link to the Doxygen HTML +# with xref:reference:boost/gil/.adoc[] links, as boostorg/redis and +# boostorg/url do. + +source-root: .. + +# Same subdirectories doc/doxyfile currently lists (RECURSIVE = NO there too). +input: + - ../include/boost/gil + - ../include/boost/gil/concepts + - ../include/boost/gil/image_processing + - ../include/boost/gil/io + - ../include/boost/gil/extension/io + - ../include/boost/gil/extension/dynamic_image +file-patterns: + - '*.hpp' + +include-symbols: + - 'boost::gil::**' +exclude-symbols: + - 'boost::gil::detail' + - 'boost::gil::**::detail' + +generator: adoc +multipage: true +base-url: https://github.com/boostorg/gil/blob/develop/ + +# GIL is header-only; this single translation unit includes everything the +# `input` filter above will look for, following the pattern documented in +# boostorg/url's mrdocs.yml (a single TU is much faster than a full project +# build and MrDocs only needs compile flags, not a real link step). +cmake: '-D BOOST_GIL_BUILD_EXAMPLES=OFF -D BOOST_GIL_BUILD_HEADER_TESTS=OFF -D BUILD_TESTING=OFF' diff --git a/doc/naming.rst b/doc/naming.rst deleted file mode 100644 index 08f74487fc..0000000000 --- a/doc/naming.rst +++ /dev/null @@ -1,48 +0,0 @@ -Naming Conventions -================== - -Description of established naming conventions used in source code of GIL, -tests and examples. - -Concrete Types --------------- - -Concrete (non-generic) GIL types follow this naming convention:: - - ColorSpace + BitDepth + [f | s]+ [c] + [_planar] + [_step] + ClassType + _t - -where: - -- ``ColorSpace`` indicates layout and ordering of components. - For example, ``rgb``, ``bgr``, ``cmyk``, ``rgba``. - -- ``BitDepth`` indicates the bit depth of the color channel. - For example, ``8``, ``16``, ``32``. - -- By default, type of channel is unsigned integral. - The ``s`` tag indicates signed integral. - The ``f`` tag indicates a floating point type, which is always signed. - -- By default, objects operate on mutable pixels. - The ``c`` tag indicates object operating over immutable pixels. - -- ``_planar`` indicates planar organization (as opposed to interleaved). - -- ``_step`` indicates special image views, locators and iterators which - traverse the data in a non-trivial way. For example, backwards or every other - pixel. - -- ``ClassType`` is ``_image`` (image), ``_view`` (image view), ``_loc`` (pixel - 2D locator) ``_ptr`` (pixel iterator), ``_ref`` (pixel reference), - ``_pixel`` (pixel value). - -- ``_t`` suffix indicates it is a name of a type. - -For example: - -.. code-block:: cpp - - bgr8_image_t a; // 8-bit interleaved BGR image - cmyk16_pixel_t b; // 16-bit CMYK pixel value; - cmyk16c_planar_ref_t c(b); // const reference to a 16-bit planar CMYK pixel. - rgb32f_planar_step_ptr_t d; // step pointer to a 32-bit planar RGB pixel. diff --git a/doc/numeric.rst b/doc/numeric.rst deleted file mode 100644 index 844a3ef3ba..0000000000 --- a/doc/numeric.rst +++ /dev/null @@ -1,7 +0,0 @@ -Numeric extension -================= - -Overview --------- - -*TODO* diff --git a/doc/package-lock.json b/doc/package-lock.json new file mode 100644 index 0000000000..ad527b2e66 --- /dev/null +++ b/doc/package-lock.json @@ -0,0 +1,1931 @@ +{ + "name": "doc", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@antora/cli": "^3.1.15", + "@antora/site-generator": "^3.1.15", + "antora": "^3.1.15" + } + }, + "node_modules/@antora/asciidoc-loader": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.2.0.tgz", + "integrity": "sha512-IaxhqSl9CvAi5Ieqt3Kc1SlJ11iq4kkWNFAAwUFCv/9L0WbzoczQ+o0y0W2lEONXVCKOBT1yzFBi55149SNwUQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/logger": "3.2.0", + "@antora/user-require-helper": "~3.0", + "@asciidoctor/core": "~2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/cli": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.2.0.tgz", + "integrity": "sha512-bJ5Vl+FMH52xm2jxdcbrEUN3aeoIYj5NGzLGsVwIPewM0RXee0kckbToAFnU0EK0Uw07cBomiJ5hwVELex0/ug==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/logger": "3.2.0", + "@antora/playbook-builder": "3.2.0", + "@antora/user-require-helper": "~3.0", + "commander": "~14.0" + }, + "bin": { + "antora": "bin/antora" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/content-aggregator": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.2.0.tgz", + "integrity": "sha512-Q7lTjNFlpQx3SWW8QbNv5P4f1DhiKhYY3PyMifRHc4npuILOU0q4+kmlO1W1swSDDCg5pDjmgwitwAi9VQgvhg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/expand-path-helper": "~3.0", + "@antora/logger": "3.2.0", + "@antora/user-require-helper": "~3.0", + "braces": "~3.0", + "cache-directory": "~2.0", + "fast-glob": "~3.3", + "hpagent": "~1.2", + "isomorphic-git": "~1.38", + "js-yaml": "~5.4", + "multi-progress": "~4.0", + "picomatch": "~4.0", + "progress": "~2.0", + "should-proxy": "~1.0", + "simple-get": "~4.0", + "vinyl": "~3.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/content-classifier": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.2.0.tgz", + "integrity": "sha512-fUrh08qpgbsjV7noFTWDQaevLC2NAs3gHRJbaTTvULEb+OZRCB0LjEPyvhIAk1wKwkfPqcnjAShJHurH/gXPCg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/asciidoc-loader": "3.2.0", + "@antora/logger": "3.2.0", + "mime-types": "~3.0", + "vinyl": "~3.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/document-converter": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.2.0.tgz", + "integrity": "sha512-+cwQxIIRNI9+BXHk2fff/jvBd17p/AHslAT02ic6tWlgRaORc5y5/dRK+Q9hSjE6vpndE92gyokRn2zsigOUDg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/asciidoc-loader": "3.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/expand-path-helper": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@antora/expand-path-helper/-/expand-path-helper-3.0.0.tgz", + "integrity": "sha512-7PdEIhk97v85/CSm3HynCsX14TR6oIVz1s233nNLsiWubE8tTnpPt4sNRJR+hpmIZ6Bx9c6QDp3XIoiyu/WYYA==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@antora/file-publisher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.2.0.tgz", + "integrity": "sha512-UqKzDI2JR/Ms88kgUteyqvIikekDBoZZGPo8L79GpvffPBP2CJmhCSSue1IuV0aFtobHuCgWqWtHzsdhy7KJ6Q==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/expand-path-helper": "~3.0", + "@antora/user-require-helper": "~3.0", + "vinyl": "~3.0", + "yazl": "~3.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/logger": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.2.0.tgz", + "integrity": "sha512-2+bTFjYOuwoWjgtIfpf8bit6OuizI7O8RnXsHzX2y6/PBbm9K8ulvmqtnFk8933P5ILTZ/NSrbgvcFz5BXrY8A==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/expand-path-helper": "~3.0", + "pino": "~10.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/navigation-builder": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.2.0.tgz", + "integrity": "sha512-W7TI0e2doDwpF7YYsYUYJtxFQfDre4egU33eOM8Y7ZEUaAZBweOCiANFgZmfZiCvM3nswLyBtMJqvVzAc8vPTg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/asciidoc-loader": "3.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/page-composer": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.2.0.tgz", + "integrity": "sha512-cBr9wYnF6ba4fbbVvs+fR/88BPoqFB9bxdU2z2kWXDsAkQIT/OHLauo+UY+H5+pw/SET1A9bOwMaNlvM4mvfxg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/logger": "3.2.0", + "handlebars": "~4.7" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/playbook-builder": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.2.0.tgz", + "integrity": "sha512-mAOrE+vDRa0wUcTc3JIZWvEai69vZ1kx6CvZi8ugSYrcWbZxNDp+R5pYWRX9037dZ5e6s5hKKGV503thm9BiSw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@iarna/toml": "~2.2", + "convict": "~6.2", + "js-yaml": "~5.4", + "json5": "~2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/redirect-producer": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.2.0.tgz", + "integrity": "sha512-soJbguowD3r7rCMn/r6OMiphXXUwNXUE9MeR6xrDa1RnoTu5mNALIhgI4mBid1CZ/jb+tnJ0WQWVArM1d9EmKw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/content-classifier": "3.2.0", + "vinyl": "~3.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/site-generator": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.2.0.tgz", + "integrity": "sha512-bgShOpARuO+1MF50HUsv25C7msyZk6GpaP2EBcjqD3s0Dj51amQL5KMqAbwfKLjTCiAUTGpC/Yb8rGeogUnmYw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/asciidoc-loader": "3.2.0", + "@antora/content-aggregator": "3.2.0", + "@antora/content-classifier": "3.2.0", + "@antora/document-converter": "3.2.0", + "@antora/file-publisher": "3.2.0", + "@antora/logger": "3.2.0", + "@antora/navigation-builder": "3.2.0", + "@antora/page-composer": "3.2.0", + "@antora/playbook-builder": "3.2.0", + "@antora/redirect-producer": "3.2.0", + "@antora/site-mapper": "3.2.0", + "@antora/site-publisher": "3.2.0", + "@antora/ui-loader": "3.2.0", + "@antora/user-require-helper": "~3.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/site-mapper": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.2.0.tgz", + "integrity": "sha512-ZnBl03Ha5dLlduVoKGC2kn+n0o8IPwoMzSbu+rLok+uzEPR2La7fQXNJa9exNzBtnE2GnNkj37jJg6YHQpIUiw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/content-classifier": "3.2.0", + "vinyl": "~3.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/site-publisher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.2.0.tgz", + "integrity": "sha512-zXYxyH0+NuhaPnGReDvL8CCmYPsocxSKn3HeE3Z39lrexK/qSajliUGYW0jP9IRi+cCFXpvuC7PDYtQfZDvWaw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/file-publisher": "3.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/ui-loader": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.2.0.tgz", + "integrity": "sha512-EYagiuG2Tk5QtnG/rwTzu/8mxDtL8J2oqZc74g0h4h3iatJeD8yNrDc8iDhNaycEZNaxPRfIK73tpKuIFZw+Iw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/expand-path-helper": "~3.0", + "braces": "~3.0", + "cache-directory": "~2.0", + "fast-glob": "~3.3", + "hpagent": "~1.2", + "js-yaml": "~5.4", + "picomatch": "~4.0", + "should-proxy": "~1.0", + "simple-get": "~4.0", + "vinyl": "~3.0", + "yauzl": "~3.4" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@antora/user-require-helper": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@antora/user-require-helper/-/user-require-helper-3.0.0.tgz", + "integrity": "sha512-KIXb8WYhnrnwH7Jj21l1w+et9k5GvcgcqvLOwxqWLEd0uVZOiMFdqFjqbVm3M+zcrs1JXWMeh2LLvxBbQs3q/Q==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/expand-path-helper": "~3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@asciidoctor/core": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@asciidoctor/core/-/core-2.2.9.tgz", + "integrity": "sha512-tIPRHo1T2SFmAm+j77cDsj0RuaszP7xJxsaVTTAF5CwKyTbazw9TnIVlpIWM5yWfIWAWcAZy92RcnPgMJwny1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asciidoctor-opal-runtime": "0.3.4", + "unxhr": "~1.2" + }, + "engines": { + "node": ">=8.11", + "npm": ">=5.0.0", + "yarn": ">=1.1.0" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "dev": true, + "license": "ISC" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/antora": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/antora/-/antora-3.2.0.tgz", + "integrity": "sha512-wRfXafDYW7FGOgwFf2vpM7cIBai/ms/XsCIrC8Rp+abScyEjlLxvoqjvYRmxRGcaJ3HdZ6mS8WntQlBVkQ9UKw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@antora/cli": "3.2.0", + "@antora/site-generator": "3.2.0" + }, + "bin": { + "antora": "bin/antora" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asciidoctor-opal-runtime": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/asciidoctor-opal-runtime/-/asciidoctor-opal-runtime-0.3.4.tgz", + "integrity": "sha512-zqd6zn1LV+PZ69AP/kEbB00zuPHMIAJY3IX8+aZV+X1qOwatYvKGjsMmdMc5ApfhtkjZ4mYkqiTPJWnEnBiMJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "~3.3", + "unxhr": "~1.2" + }, + "engines": { + "node": ">=8.11" + } + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/bare-events": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cache-directory": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cache-directory/-/cache-directory-2.0.0.tgz", + "integrity": "sha512-7YKEapH+2Uikde8hySyfobXBqPKULDyHNl/lhKm7cKf/GJFdG/tU/WpLrOg2y9aUrQrWUilYqawFIiGJPS6gDA==", + "dev": true, + "license": "LGPL-3.0+", + "dependencies": { + "xdg-basedir": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/clean-git-ref": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/convict": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.5.tgz", + "integrity": "sha512-JtXpxqDqJ8P0UwEHwhxLzCIXQy97vlYBZR222Sbzb1q1Erex9ASrztJ29SyhWFQjod1AeFBaPzEEC8YvtZMIYg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "yargs-parser": "^20.2.7" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/diff3": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hpagent": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", + "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isomorphic-git": { + "version": "1.38.10", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.38.10.tgz", + "integrity": "sha512-nJSSq7ypu97vM33rSdxXyPzSWksCberjKspzXhFEcBHdVyxdNkWByAzJ/AURV1jIlfv8pLq37lGdIEt7ORj17Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-lock": "^1.4.1", + "clean-git-ref": "^2.0.1", + "crc-32": "^1.2.0", + "diff3": "0.0.3", + "ignore": "^5.1.4", + "minimisted": "^2.0.0", + "pako": "^1.0.10", + "pify": "^4.0.1", + "readable-stream": "^4.0.0", + "sha.js": "^2.4.12", + "simple-get": "^4.0.1" + }, + "bin": { + "isogit": "cli.cjs" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/js-yaml": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", + "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimisted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + } + }, + "node_modules/multi-progress": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multi-progress/-/multi-progress-4.0.0.tgz", + "integrity": "sha512-9zcjyOou3FFCKPXsmkbC3ethv51SFPoA4dJD6TscIp2pUmy26kBDZW6h9XofPELrzseSkuD7r0V+emGEeo39Pg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "progress": "^2.0.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pino": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^4.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "dev": true, + "license": "MIT" + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-warning": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz", + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true, + "license": "ISC" + }, + "node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/should-proxy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/should-proxy/-/should-proxy-1.0.4.tgz", + "integrity": "sha512-RPQhIndEIVUCjkfkQ6rs6sOR6pkxJWCNdxtfG5pP0RVgUYbK5911kLTF0TNcCC0G3YCGd492rMollFT2aTd9iQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/streamx": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/thread-stream": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz", + "integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "real-require": "^1.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/thread-stream/node_modules/real-require": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz", + "integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unxhr": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unxhr/-/unxhr-1.2.0.tgz", + "integrity": "sha512-6cGpm8NFXPD9QbSNx0cD2giy7teZ6xOkCUH3U89WKVkL9N9rBrWjlCwhR94Re18ZlAop4MOc3WU1M3Hv/bgpIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.11" + } + }, + "node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yauzl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yazl": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-3.3.1.tgz", + "integrity": "sha512-BbETDVWG+VcMUle37k5Fqp//7SDOK2/1+T7X8TD96M3D9G8jK5VLUdQVdVjGi8im7FGkazX7kk5hkU8X4L5Bng==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "^1.0.0" + } + } + } +} diff --git a/doc/package.json b/doc/package.json new file mode 100644 index 0000000000..6643a96f68 --- /dev/null +++ b/doc/package.json @@ -0,0 +1,7 @@ +{ + "devDependencies": { + "@antora/cli": "^3.1.15", + "@antora/site-generator": "^3.1.15", + "antora": "^3.1.15" + } +} diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index bd6bea97a6..0000000000 --- a/doc/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -sphinx>=2.1 diff --git a/doc/tutorial/gradient.rst b/doc/tutorial/gradient.rst deleted file mode 100644 index f1dabc9d0c..0000000000 --- a/doc/tutorial/gradient.rst +++ /dev/null @@ -1,1007 +0,0 @@ -Tutorial: Image Gradient -======================== - -.. contents:: - :local: - :depth: 1 - -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 -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: - -.. code-block:: cpp - - D[x] = (I[x-1] - I[x+1]) / 2 - -For simplicity, we will also ignore the boundary cases - the pixels along the -edges of the image for which one of the neighbors is not defined. The focus of -this document is how to use GIL, not how to create a good gradient generation -algorithm. - -Interface and Glue Code ------------------------ - -Let us first start with 8-bit unsigned grayscale image as the input and 8-bit -signed grayscale image as the output. - -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) - { - assert(src.dimensions() == dst.dimensions()); - ... // compute the gradient - } - -``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 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 -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. -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 -their constness to their pixels - a constant reference to an image will not -allow for modifying its pixels. - -Most GIL algorithms operate on image views; images are rarely -needed. GIL's design is very similar to that of the STL. The STL -equivalent of GIL's image is a container, like ``std::vector``, -whereas GIL's image view corresponds to STL range, which is often -represented with a pair of iterators. STL algorithms operate on -ranges, just like GIL algorithms operate on image views. - -GIL's image views can be constructed from raw data - the dimensions, -the number of bytes per row and the pixels, which for chunky views are -represented with one pointer. Here is how to provide the glue between -your code and GIL: - -.. code-block:: cpp - - void ComputeXGradientGray8( - unsigned char const* src_pixels, std::ptrdiff_t src_row_bytes, - int w, int h, - signed char* dst_pixels, std::ptrdiff_t 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); - } - -This glue code is very fast and views are lightweight - in the above example -the views have a size of 16 bytes. They consist of a pointer to the top left -pixel and three integers - the width, height, and number of bytes per row. - -First Implementation --------------------- - -Focusing on simplicity at the expense of speed, we can compute the horizontal -gradient like this: - -.. code-block:: cpp - - void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) - { - for (int y = 0; y < src.height(); ++y) - for (int x = 1; x < src.width() - 1; ++x) - dst(x, y) = (src(x-1, y) - src(x+1, y)) / 2; - } - -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. -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.) - -While the above code is easy to read, it is not very fast, because the binary -``operator()`` computes the location of the pixel in a 2D grid, which involves -addition and multiplication. Here is a faster version of the above: - -.. code-block:: cpp - - void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) - { - 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); - - for (int x = 1; x < src.width() - 1; ++x) - dst_it[x] = (src_it[x-1] - src_it[x+1]) / 2; - } - } - -We use pixel iterators initialized at the beginning of each row. GIL's -iterators are Random Access Traversal iterators. If you are not -familiar with random access iterators, think of them as if they were -pointers. In fact, in the above example the two iterator types are raw -C pointers and their ``operator[]`` is a fast pointer indexing -operator. - -The code to compute gradient in the vertical direction is very -similar: - -.. code-block:: cpp - - void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) - { - for (int x = 0; x < src.width(); ++x) - { - gray8c_view_t::y_iterator src_it = src.col_begin(x); - gray8s_view_t::y_iterator dst_it = dst.col_begin(x); - - for (int y = 1; y < src.height() - 1; ++y) - dst_it[y] = (src_it[y-1] - src_it[y+1]) / 2; - } - } - -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 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; -traversing an image vertically results in lots of cache misses. A much more -efficient and cache-friendly version will iterate over the columns in the inner -loop: - -.. code-block:: cpp - - void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) - { - for (int y = 1; y < src.height() - 1; ++y) - { - gray8c_view_t::x_iterator src1_it = src.row_begin(y-1); - gray8c_view_t::x_iterator src2_it = src.row_begin(y+1); - gray8s_view_t::x_iterator dst_it = dst.row_begin(y); - - for (int x = 0; x < src.width(); ++x) - { - *dst_it = ((*src1_it) - (*src2_it)) / 2; - ++dst_it; - ++src1_it; - ++src2_it; - } - } - } - -This sample code also shows an alternative way of using pixel iterators - -instead of ``operator[]`` one could use increments and dereferences. - -Using Locators --------------- - -Unfortunately this cache-friendly version requires the extra hassle of -maintaining two separate iterators in the source view. For every pixel, we -want to access its neighbors above and below it. Such relative access can be -done with GIL locators: - -.. code-block:: cpp - - void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) - { - 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); - - for (int x = 0; x < src.width(); ++x) - { - *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_t(-src.width(), 1); // carriage return - } - } - -The first line creates a locator pointing to the first pixel of the -second row of the source view. A GIL pixel locator is very similar to -an iterator, except that it can move both horizontally and -vertically. ``src_loc.x()`` and ``src_loc.y()`` return references to a -horizontal and a vertical iterator respectively, which can be used to -move the locator along the desired dimension, as shown -above. Additionally, the locator can be advanced in both dimensions -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 - 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 -computations than necessary. The code ``src_loc(0,1)`` has to compute -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: - -.. code-block:: cpp - - void y_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) - { - gray8c_view_t::xy_locator src_loc = src.xy_at(0,1); - gray8c_view_t::xy_locator::cached_location_t above = src_loc.cache_location(0,-1); - gray8c_view_t::xy_locator::cached_location_t below = src_loc.cache_location(0, 1); - - for (int y = 1; y < src.height() - 1; ++y) - { - 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.x(); - } - src_loc += point_t(-src.width(), 1); - } - } - -In this example ``src_loc[above]`` corresponds to a fast pointer indexing -operation and the code is efficient. - -Creating a Generic Version of GIL Algorithms --------------------------------------------- - -Let us make our ``x_gradient`` more generic. It should work with any image -views, as long as they have the same number of channels. The gradient -operation is to be computed for each channel independently. - -Here is how the new interface looks like: - -.. code-block:: cpp - - template - void x_gradient(SrcView const& src, DstView const& dst) - { - gil_function_requires>(); - gil_function_requires>(); - gil_function_requires - < - ColorSpacesCompatibleConcept - < - typename color_space_type::type, - typename color_space_type::type - > - >(); - - ... // compute the gradient - } - -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 -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 -(i.e. have the same set of channels). - -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 -set of requirements; in other words, they have to \emph model the -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 -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 -``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 -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 -using concept checks is the sometimes severe impact they have on -compile time. This is why GIL performs concept checks only in debug -mode, and only if ``BOOST_GIL_USE_CONCEPT_CHECK`` is defined (off by -default). - -The body of the generic function is very similar to that of the -concrete one. The biggest difference is that we need to loop over the -channels of the pixel and compute the gradient for each channel: - -.. code-block:: cpp - - template - void x_gradient(SrcView const& src, DstView const& dst) - { - 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 (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; - } - } - -Having an explicit loop for each channel could be a performance problem. -GIL allows us to abstract out such per-channel operations: - -.. code-block:: cpp - - template - struct halfdiff_cast_channels - { - template - auto operator()(T const& in1, T const& in2) const -> Out - { - return Out((in1 - in2) / 2); - } - }; - - template - void x_gradient(SrcView const& src, DstView const& dst) - { - using dst_channel_t = typename channel_type::type; - - 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) - { - static_transform(src_it[x-1], src_it[x+1], dst_it[x], - halfdiff_cast_channels()); - } - } - } - -The ``static_transform`` is an example of a channel-level GIL algorithm. -Other such algorithms are ``static_generate``, ``static_fill`` and -``static_for_each``. They are the channel-level equivalents of STL -``generate``, ``transform``, ``fill`` and ``for_each`` respectively. -GIL channel algorithms use static recursion to unroll the loops; they never -loop over the channels explicitly. - -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: - -.. code-block:: cpp - - // Calling with 16-bit grayscale data - void XGradientGray16_Gray32( - unsigned short const* src_pixels, std::ptrdiff_t src_row_bytes, - int w, int h, - signed int* dst_pixels, std::ptrdiff_t 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, std::ptrdiff_t src_row_bytes, - int w, int h, - signed short* dst_pixels, std::ptrdiff_t 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, - 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, reinterpret_cast(dst_pixels), dst_row_bytes); - x_gradient(src,dst); - } - -As these examples illustrate, both the source and the destination can be -interleaved or planar, of any channel depth (assuming the destination channel -is assignable to the source), and of any compatible color spaces. - -GIL 2.1 can also natively represent images whose channels are not -byte-aligned, such as 6-bit RGB222 image or a 1-bit Gray1 image. -GIL algorithms apply to these images natively. See the design guide or sample -files for more on using such images. - -Image View Transformations --------------------------- - -One way to compute the y-gradient is to rotate the image by 90 degrees, -compute the x-gradient and rotate the result back. -Here is how to do this in GIL: - -.. code-block:: cpp - - template - void y_gradient(SrcView const& src, DstView const& dst) - { - x_gradient(rotated90ccw_view(src), rotated90ccw_view(dst)); - } - -``rotated90ccw_view`` takes an image view and returns an image view -representing 90-degrees counter-clockwise rotation of its input. It is -an example of a GIL view transformation function. GIL provides a -variety of transformation functions that can perform any axis-aligned -rotation, transpose the view, flip it vertically or horizontally, -extract a rectangular subimage, perform color conversion, subsample -view, etc. The view transformation functions are fast and shallow - -they don't copy the pixels, they just change the "coordinate system" -of accessing the pixels. ``rotated90cw_view``, for example, returns a -view whose horizontal iterators are the vertical iterators of the -original view. The above code to compute ``y_gradient`` is slow -because of the memory access pattern; using ``rotated90cw_view`` does -not make it any slower. - -Another example: suppose we want to compute the gradient of the N-th -channel of a color image. Here is how to do that: - -.. code-block:: cpp - - template - 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 -a step view - a view whose horizontal iterator skips over two channels -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 -pixels in the view, use: - -.. code-block:: cpp - - 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 -represented as a single subsampled view whose step is the product of -the steps of the two views. - -1D pixel iterators ------------------- - -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 -nice and instructional to see how we can rewrite it in canonical -form. The way to do that in GIL is to write a version that works for -every pixel, but apply it only on the subimage that excludes the first -and last column: - -.. code-block:: cpp - - void x_gradient_unguarded(gray8c_view_t const& src, gray8s_view_t const& dst) - { - 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); - - for (int x = 0; x < src.width(); ++x) - dst_it[x] = (src_it[x-1] - src_it[x+1]) / 2; - } - } - - void x_gradient(gray8c_view_t const& src, gray8s_view_t const& dst) - { - 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())); - } - -``subimage_view`` is another example of a GIL view transformation -function. It takes a source view and a rectangular region (in this -case, defined as x_min,y_min,width,height) and returns a view -operating on that region of the source view. The above implementation -has no measurable performance degradation from the version that -operates on the original views. - -Now that ``x_gradient_unguarded`` operates on every pixel, we can -rewrite it more compactly: - -.. code-block:: cpp - - 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) - *dst_it = (src_it.x()[-1] - src_it.x()[1]) / 2; - } - -GIL image views provide ``begin()`` and ``end()`` methods that return -one dimensional pixel iterators which iterate over each pixel in the -view, left to right and top to bottom. They do a proper "carriage -return" - they skip any unused bytes at the end of a row. As such, -they are slightly suboptimal, because they need to keep track of their -current position with respect to the end of the row. Their increment -operator performs one extra check (are we at the end of the row?), a -check that is avoided if two nested loops are used instead. These -iterators have a method ``x()`` which returns the more lightweight -horizontal iterator that we used previously. Horizontal iterators have -no notion of the end of rows. In this case, the horizontal iterators -are raw C pointers. In our example, we must use the horizontal -iterators to access the two neighbors properly, since they could -reside outside the image view. - -STL Equivalent Algorithms -------------------------- - -GIL provides STL equivalents of many algorithms. For example, -``std::transform`` is an STL algorithm that sets each element in a -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 -abstract that operation in a function object, we can use GIL's -``transform_pixel_positions`` to do that: - -.. code-block:: cpp - - struct half_x_difference - { - auto operator()(gray8c_loc_t const& src_loc) const -> int - { - return (src_loc.x()[-1] - src_loc.x()[1]) / 2; - } - }; - - void x_gradient_unguarded(gray8c_view_t const& src, gray8s_view_t const& dst) - { - transform_pixel_positions(src, dst, half_x_difference()); - } - -GIL provides the algorithms ``for_each_pixel`` and -``transform_pixels`` which are image view equivalents of STL -``std::for_each`` and ``std::transform``. It also provides -``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 -through the pixels using the more efficient two nested loops (as -opposed to the single loop using 1-D iterators). - -Color Conversion ----------------- - -Instead of computing the gradient of each color plane of an image, we -often want to compute the gradient of the luminosity. In other words, -we want to convert the color image to grayscale and compute the -gradient of the result. Here is how to compute the luminosity gradient of -a 32-bit float RGB image: - -.. code-block:: cpp - - void x_gradient_rgb_luminosity(rgb32fc_view_t const& src, gray8s_view_t const& dst) - { - x_gradient(color_converted_view(src), dst); - } - -``color_converted_view`` is a GIL view transformation function that -takes any image view and returns a view in a target color space and -channel depth (specified as template parameters). In our example, it -constructs an 8-bit integer grayscale view over 32-bit float RGB -pixels. Like all other view transformation functions, -``color_converted_view`` is very fast and shallow. It doesn't copy the -data or perform any color conversion. Instead it returns a view that -performs color conversion every time its pixels are accessed. - -In the generic version of this algorithm we might like to convert the -color space to grayscale, but keep the channel depth the same. We do -that by constructing the type of a GIL grayscale pixel with the same -channel as the source, and color convert to that pixel type: - -.. code-block:: cpp - - template - void x_luminosity_gradient(SrcView const& src, DstView const& dst) - { - using gray_pixel_t = pixel::type, gray_layout_t>; - x_gradient(color_converted_view(src), dst); - } - -When the destination color space and channel type happens to be the -same as the source one, color conversion is unnecessary. GIL detects -this case and avoids calling the color conversion code at all - -i.e. ``color_converted_view`` returns back the source view unchanged. - -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 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: - -.. code-block:: cpp - - void x_luminosity_gradient(rgb32fc_view_t const& src, gray8s_view_t const& dst) - { - gray8_image_t ccv_image(src.dimensions()); - copy_pixels(color_converted_view(src), view(ccv_image)); - - x_gradient(const_view(ccv_image), dst); - } - -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 -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. - -Creating a generic version of the above is a bit trickier: - -.. code-block:: cpp - - template - void x_luminosity_gradient(SrcView const& src, DstView const& dst) - { - using d_channel_t = typename channel_type::type; - using channel_t = typename detail::channel_convert_to_unsigned::type; - using gray_pixel_t = pixel; - using gray_image_t = image; - - gray_image_t ccv_image(src.dimensions()); - copy_pixels(color_converted_view(src), view(ccv_image)); - x_gradient(const_view(ccv_image), dst); - } - -First we use the ``channel_type`` metafunction to get the channel type -of the destination view. A metafunction is a function operating on -types. In GIL metafunctions are class templates (declared with -``struct`` type specifier) which take their parameters as template -parameters and return their result in a nested typedef called -``type``. In this case, ``channel_type`` is a unary metafunction which -in this example is called with the type of an image view and returns -the type of the channel associated with that image view. - -GIL constructs that have an associated pixel type, such as pixels, -pixel iterators, locators, views and images, all model -``PixelBasedConcept``, which means that they provide a set of -metafunctions to query the pixel properties, such as ``channel_type``, -``color_space_type``, ``channel_mapping_type``, and ``num_channels``. - -After we get the channel type of the destination view, we use another -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 -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: - -.. code-block:: cpp - - template - void x_luminosity_gradient(SrcView const& src, DstView const& dst) - { - 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)); - x_gradient(const_view(ccv_image), dst); - } - -GIL provides a set of metafunctions that generate GIL types - -``image_type`` is one such meta-function that constructs the type of -an image from a given channel type, color layout, and -planar/interleaved option (the default is interleaved). There are also -similar meta-functions to construct the types of pixel references, -iterators, locators and image views. GIL also has metafunctions -``derived_pixel_reference_type``, ``derived_iterator_type``, -``derived_view_type`` and ``derived_image_type`` that construct the -type of a GIL construct from a given source one by changing one or -more properties of the type and keeping the rest. - -From the image type we can use the nested typedef ``value_type`` to -obtain the type of a pixel. GIL images, image views and locators have -nested typedefs ``value_type`` and ``reference`` to obtain the type of -the pixel and a reference to the pixel. If you have a pixel iterator, -you can get these types from its ``iterator_traits``. Note also the -algorithm ``copy_and_convert_pixels``, which is an abbreviated version -of ``copy_pixels`` with a color converted source view. - -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 -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 - { - 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() = 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) {} - - auto operator()(point_t const& p) const -> result_type - { - // normalize the coords to (-2..1, -1.5..1.5) - // (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; - - auto get_num_iter(point const& p) const -> double - { - point Z(0,0); - 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/static_cast(MAX_ITER); - } - return 0; - } - }; - -We can now use GIL's ``virtual_2d_locator`` with this function object -to construct a rgb8 Mandelbrot view of size 200x200 pixels: - -.. code-block:: cpp - - 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), - 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_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_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) - 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: - -.. image:: ../images/mandel.jpg - -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, -however, the properties of an image view, such as its color space and -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 -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 -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. - -Let's 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. - -First, we need to make a function object that contains the templated -destination view and has an application operator taking a templated -source view: - -.. code-block:: cpp - - #include - - template - struct x_gradient_obj - { - using result_type = void; // required typedef - - DstView const& _dst; - x_gradient_obj(DstView const& dst) : _dst(dst) {} - - template - 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 an image view variant and calls ``variant2::visit`` passing it the -function object: - -.. code-block:: cpp - - template - void x_luminosity_gradient(any_image_view const& src, DstView const& 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 -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 -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 -statement. Algorithms that perform an operation for each pixel in an -image view have practically no performance degradation when used with -a variant. - -Here is how we can construct a variant and invoke the algorithm: - -.. code-block:: cpp - - #include - - 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)); - 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 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 ``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 -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)`` -returns an ``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 -instantiated with every possible combination of the two input types! -This can take a toll on both the compile time and the executable size. - -Conclusion ----------- - -This tutorial provides a glimpse at the challenges associated with -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 -the algorithm can work with fully abstracted virtual images, and even -images whose type is specified at run time. The associated video -presentation also demonstrates that even for complex scenarios the -generated assembly is comparable to that of a C version of the -algorithm, hand-written for the specific image types. - -Yet, even for such a simple algorithm, we are far from making a fully -generic and optimized code. In particular, the presented algorithms -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 -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 -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, -``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 -well. Channel-level operations, such as the half-difference, could be -abstracted out into atomic channel-level algorithms and performance -overloads could be provided for concrete channel -types. Processor-specific operations could be used, for example, to -perform the operation over an entire row of pixels simultaneously, or -the data could be pre-fetched. All of these optimizations can be -realized as performance specializations of the generic -algorithm. Finally, compilers, while getting better over time, are -still failing to fully optimize generic code in some cases, such as -failing to inline some functions or put some variables into -registers. If performance is an issue, it might be worth trying your -code with different compilers. diff --git a/doc/tutorial/histogram.rst b/doc/tutorial/histogram.rst deleted file mode 100644 index 582eb432e0..0000000000 --- a/doc/tutorial/histogram.rst +++ /dev/null @@ -1,103 +0,0 @@ -Tutorial: Histogram -=================== - -.. contents:: - :local: - :depth: 1 - -This is a short tutorial presenting an example of a very simple sample of code -from an existing code base that calculates the histogram of an image. -Next, the program is rewritten using GIL features. - -Original implementation ------------------------ - -Actual code from a commercial software product that computes the luminosity -histogram (variable names have been changed and unrelated parts removed): - -.. code-block:: cpp - - void luminosity_hist( - std::uint8_t const* r, std::uint8_t const* g, std::uint8_t const* b, - int rows, int cols, int sRowBytes, Histogram* hist) - { - for (int r = 0; r < rows; r++) - { - for (int c = 0; c < cols; c++) - { - int v = RGBToGray(r[c], g[c], b[c]); // call internal function or macro - (*hist)[v]++; - } - r += sRowBytes; - g += sRowBytes; - b += sRowBytes; - } - } - -Let's consider the following issues of the implementation above: - -- Works only for RGB (duplicate versions exist for other color spaces) -- Works only for 8-bit images (duplicate versions exist) -- Works only for planar images - -GIL implementation ------------------- - -.. code-block:: cpp - - template - void grayimage_histogram(GrayView& img, R& hist) - { - for (typename GrayView::iterator it=img.begin(); it!=img.end(); ++it) - ++hist[*it]; - } - - template - void luminosity8bit_hist(View& img, R& hist) - { - grayimage_histogram(color_converted_view(img),hist); - } - -Using the Boost.Lambda library (or C++11 lambda) features it can be written -even simpler: - -.. code-block:: cpp - - using boost::lambda; - - template - void grayimage_histogram(GrayView& img, R& hist) - { - for_each_pixel(img, ++var(hist)[_1]); - } - -Let's consider the following advantages of the GIL version: - -- Works with any supported channel depth, color space, channel ordering - (RGB vs BGR), and row alignment policy. -- Works for both planar and interleaved images. -- Works with new color spaces, channel depths and image types that can be - provided in future extensions of GIL -- The second version is as efficient as the hand-coded version - -Shortly, it is also very flexible. - -For example, to compute the histogram of the second channel of the top left -quadrant of the image, taking every other row and column, call: - -.. code-block:: cpp - - grayimage_histogram( - nth_channel_view( - subsampled_view( - subimage_view(img, - 0,0, img.width() / 2, img.height() / 2), // upper left quadrant - 2, 2 // skip every other row and column - ), - 1 // index of the second channel (for example, green for RGB) - ), - hist - ); - -Since GIL operates on the source pixels of ``img`` object directly, no extra -memory is allocated and no images are copied. diff --git a/doc/tutorial/video.rst b/doc/tutorial/video.rst deleted file mode 100644 index 88eb742126..0000000000 --- a/doc/tutorial/video.rst +++ /dev/null @@ -1,12 +0,0 @@ -Tutorial: Video Lecture -======================= - -This is original video lecture about GIL presented by Lubomir Bourdev. - -.. raw:: html - -
- -
- -Source link: https://www.youtube.com/watch?v=sR8Wjg0pceE From 7439ac48d7eae51f091398f2086c20680db6a3d1 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 10:41:24 +0200 Subject: [PATCH 20/37] docs: wire in the real MrDocs API reference Replace the interim Doxygen reference (see previous commit) with a real MrDocs-generated one, now that the upstream segfault blocking it is fixed (verified against a fixed MrDocs dev build against all 49 previously-crashing GIL headers, and against the full public API: 6796 declarations, 5662 pages). - doc/mrdocs-src/all.cpp: the single translation unit MrDocs extracts from -- core + concepts + image_processing + the dynamic_image/toolbox/numeric/ histogram extensions + the generic io layer. Deliberately excludes extension/io/{jpeg,png,tiff,bmp,pnm,raw}, which need the matching external image libraries just to parse. - doc/mrdocs.yml: scoped to that same surface; compilation-database is generated by build_antora.sh since it needs $BOOST_SRC_DIR baked in. - antora.yml: ext.cppReference wired in (camelCase -- the extension doesn't read the kebab-case key the public docs guide shows). - reference.adoc: rewritten from the Doxygen stopgap to a curated table of xref:reference:boost/gil/.adoc[] links, grouped by the categories the old doc/reference.dox Doxygen-group taxonomy used (that taxonomy has no equivalent in MrDocs' output, which groups by namespace/file instead). - design/*.adoc: the ~45 concept deep-links that used to point at Doxygen HTML now point at the real MrDocs reference pages instead. - doxyfile, reference.dox, header.html, footer.html, doxygen-boost.css: removed, no longer needed. - local-playbook.yml: failure_level set to `fatal` rather than `error` -- some MrDocs-generated pages (e.g. packed_pixel) link to private-member sub-pages (`_bitfield`) that MrDocs doesn't actually emit regardless of extract-private; cosmetic, worth another small upstream report, but not worth hard-failing the build over. - .gitignore: `/doc/*build*` was accidentally also matching and silently untracking doc/build_antora.sh this whole time (the same trap the pre-existing `!build.jam` exception below it was clearly added for before); narrowed to `/doc/build/`, and doc/build_antora.sh is now actually tracked. Co-Authored-By: Claude Sonnet 5 --- .github/actions/docs-prerequisites/action.yml | 2 - .gitignore | 2 +- doc/antora.yml | 10 +- doc/build_antora.sh | 55 ++++ doc/doxyfile | 232 -------------- doc/doxygen-boost.css | 3 - doc/footer.html | 22 -- doc/header.html | 34 -- doc/local-playbook.yml | 19 +- doc/modules/ROOT/pages/design/channel.adoc | 10 +- .../ROOT/pages/design/color_space.adoc | 6 +- doc/modules/ROOT/pages/design/image.adoc | 6 +- doc/modules/ROOT/pages/design/image_view.adoc | 14 +- doc/modules/ROOT/pages/design/pixel.adoc | 18 +- .../ROOT/pages/design/pixel_iterator.adoc | 16 +- .../ROOT/pages/design/pixel_locator.adoc | 16 +- doc/modules/ROOT/pages/design/point.adoc | 4 +- doc/modules/ROOT/pages/reference.adoc | 149 ++++++++- doc/mrdocs-src/all.cpp | 45 +++ doc/mrdocs.yml | 44 ++- doc/package-lock.json | 297 +++++++++++++----- doc/package.json | 3 + doc/reference.dox | 281 ----------------- 23 files changed, 545 insertions(+), 743 deletions(-) create mode 100644 doc/build_antora.sh delete mode 100644 doc/doxyfile delete mode 100644 doc/doxygen-boost.css delete mode 100644 doc/footer.html delete mode 100644 doc/header.html create mode 100644 doc/mrdocs-src/all.cpp delete mode 100644 doc/reference.dox diff --git a/.github/actions/docs-prerequisites/action.yml b/.github/actions/docs-prerequisites/action.yml index 58fdbd9535..c52a8fbfc2 100644 --- a/.github/actions/docs-prerequisites/action.yml +++ b/.github/actions/docs-prerequisites/action.yml @@ -3,8 +3,6 @@ description: 'Downloads all the necessary packages for building documentation' runs: using: composite steps: - - run: sudo apt-get install doxygen - shell: bash - uses: actions/setup-node@v4 with: node-version: 20 diff --git a/.gitignore b/.gitignore index 1d10f0eb9b..bbc4675887 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ # Boost.Build stdcerr -/doc/*build* +/doc/build/ /io/output # Documentation (Antora + Doxygen) diff --git a/doc/antora.yml b/doc/antora.yml index 5ada83e0bf..bc7cf3b6b8 100644 --- a/doc/antora.yml +++ b/doc/antora.yml @@ -13,10 +13,6 @@ title: Boost.GIL start_page: index.adoc nav: - modules/ROOT/nav.adoc - -# NOTE: the C++ API reference is not yet generated by MrDocs. -# MrDocs 0.8.0 segfaults on most of GIL's headers (pixel/image/locator/iterator -# families, i.e. anything built on the CRTP proxy-reference pattern) -- see the -# upstream report before re-enabling `ext: cpp-reference: config: doc/mrdocs.yml` -# here. Until then, modules/ROOT/pages/reference.adoc links to the Doxygen HTML -# reference generated alongside this Antora build (see build_antora.sh). +ext: + cppReference: + config: doc/mrdocs.yml diff --git a/doc/build_antora.sh b/doc/build_antora.sh new file mode 100644 index 0000000000..8fd11624d4 --- /dev/null +++ b/doc/build_antora.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# +# Copyright (c) 2026 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) +# +# Official repository: https://github.com/boostorg/gil +# + +set -e + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +cd "$SCRIPT_DIR" + +if [ -z "${PLAYBOOK:-}" ]; then + PLAYBOOK="local-playbook.yml" +fi + +# Required by the @cppalliance/antora-cpp-reference-extension (see +# local-playbook.yml): a full Boost superproject checkout, so GIL's headers +# resolve cross-library includes (boost/mp11.hpp, boost/stl_interfaces/...). +# `system-env` on that extension's dependency entry means it reuses this +# instead of cloning its own -- CI's setup-boost action already produces one +# at ../../.. from here (boost-root/libs/gil/doc), same as this fallback. +export BOOST_SRC_DIR="${BOOST_SRC_DIR:-$(realpath "$SCRIPT_DIR/../../..")}" +echo "Using BOOST_SRC_DIR=$BOOST_SRC_DIR" + +echo "Generating the MrDocs compilation database..." +# See mrdocs.yml / mrdocs-src/all.cpp: a single translation unit is enough +# for MrDocs to extract the whole public API, and is far faster than trying +# to point it at GIL's real CMakeLists.txt (which needs libjpeg/libpng/ +# libtiff just to configure, for the extension/io backends this deliberately +# excludes -- see mrdocs-src/all.cpp for why). +# The file must be literally named compile_commands.json -- MrDocs rejects +# any other basename even when given as a full, valid, existing path. +cat > compile_commands.json < - - - - - - - - - - diff --git a/doc/header.html b/doc/header.html deleted file mode 100644 index 4787582f06..0000000000 --- a/doc/header.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - $projectname: $title - $title - - - - $treeview - $search - $mathjax - - $extrastylesheet - - -
- - - - - - -
-

Boost GIL

-

$searchbox
-
-
-
- diff --git a/doc/local-playbook.yml b/doc/local-playbook.yml index 81b8fc7361..768ac3499e 100644 --- a/doc/local-playbook.yml +++ b/doc/local-playbook.yml @@ -23,6 +23,16 @@ content: start_path: doc edit_url: 'https://github.com/boostorg/gil/edit/{refname}/{path}' +antora: + extensions: + - require: '@cppalliance/antora-cpp-reference-extension' + dependencies: + - name: 'boost' + repo: 'https://github.com/boostorg/boost.git' + tag: 'develop' + variable: 'BOOST_SRC_DIR' + system-env: 'BOOST_SRC_DIR' + ui: bundle: url: https://github.com/boostorg/website-v2-docs/releases/download/ui-master/ui-bundle.zip @@ -37,4 +47,11 @@ output: runtime: log: - failure_level: error + # `fatal`, not `error`: some MrDocs-generated reference pages link to + # private-member sub-pages (e.g. packed_pixel's `_bitfield`) that MrDocs + # doesn't actually emit, regardless of extract-private -- a small, + # cosmetic upstream issue unrelated to this migration, logged as `error` + # by Antora. `failure_level` is a floor, not a ceiling: `error` messages + # still fail the build at `warn`. Once the MrDocs issue is fixed this + # should go back to `error`. + failure_level: fatal diff --git a/doc/modules/ROOT/pages/design/channel.adoc b/doc/modules/ROOT/pages/design/channel.adoc index 41d6c13caa..cd8da398a0 100644 --- a/doc/modules/ROOT/pages/design/channel.adoc +++ b/doc/modules/ROOT/pages/design/channel.adoc @@ -86,11 +86,11 @@ such as support for arithmetic operations. ==== See also: -* link:../reference/structboost_1_1gil_1_1_channel_concept.html[ChannelConcept] -* link:../reference/structboost_1_1gil_1_1_channel_value_concept.html[ChannelValueConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_channel_concept.html[MutableChannelConcept] -* link:../reference/structboost_1_1gil_1_1_channels_compatible_concept.html[ChannelsCompatibleConcept] -* link:../reference/structboost_1_1gil_1_1_channel_convertible_concept.html[ChannelConvertibleConcept] +* xref:reference:boost/gil/ChannelConcept.adoc[ChannelConcept] +* xref:reference:boost/gil/ChannelValueConcept.adoc[ChannelValueConcept] +* xref:reference:boost/gil/MutableChannelConcept.adoc[MutableChannelConcept] +* xref:reference:boost/gil/ChannelsCompatibleConcept.adoc[ChannelsCompatibleConcept] +* xref:reference:boost/gil/ChannelConvertibleConcept.adoc[ChannelConvertibleConcept] ==== == Models diff --git a/doc/modules/ROOT/pages/design/color_space.adoc b/doc/modules/ROOT/pages/design/color_space.adoc index 59ec3185c2..21e299ed56 100644 --- a/doc/modules/ROOT/pages/design/color_space.adoc +++ b/doc/modules/ROOT/pages/design/color_space.adoc @@ -13,9 +13,9 @@ same set of colors in the same order). ==== See also: -* link:../reference/structboost_1_1gil_1_1_color_space_concept.html[ColorSpaceConcept] -* link:../reference/structboost_1_1gil_1_1_color_spaces_compatible_concept.html[ColorSpacesCompatibleConcept] -* link:../reference/structboost_1_1gil_1_1_channel_mapping_concept.html[ChannelMappingConcept] +* xref:reference:boost/gil/ColorSpaceConcept.adoc[ColorSpaceConcept] +* xref:reference:boost/gil/ColorSpacesCompatibleConcept.adoc[ColorSpacesCompatibleConcept] +* xref:reference:boost/gil/ChannelMappingConcept.adoc[ChannelMappingConcept] ==== == Models diff --git a/doc/modules/ROOT/pages/design/image.adoc b/doc/modules/ROOT/pages/design/image.adoc index 3d4c77e2ed..26a4f78b8f 100644 --- a/doc/modules/ROOT/pages/design/image.adoc +++ b/doc/modules/ROOT/pages/design/image.adoc @@ -72,9 +72,9 @@ because immutable images are not very useful. ==== See also: -* link:../reference/structboost_1_1gil_1_1_random_access_n_d_image_concept.html[RandomAccessNDImageConcept] -* link:../reference/structboost_1_1gil_1_1_random_access2_d_image_concept.html[RandomAccess2DImageConcept] -* link:../reference/structboost_1_1gil_1_1_image_concept.html[ImageConcept] +* xref:reference:boost/gil/RandomAccessNDImageConcept.adoc[RandomAccessNDImageConcept] +* xref:reference:boost/gil/RandomAccess2DImageConcept.adoc[RandomAccess2DImageConcept] +* xref:reference:boost/gil/ImageConcept.adoc[ImageConcept] ==== == Models diff --git a/doc/modules/ROOT/pages/design/image_view.adoc b/doc/modules/ROOT/pages/design/image_view.adoc index bc0ab1ea00..b94918a996 100644 --- a/doc/modules/ROOT/pages/design/image_view.adoc +++ b/doc/modules/ROOT/pages/design/image_view.adoc @@ -151,13 +151,13 @@ compatible. ==== See also: -* link:../reference/structboost_1_1gil_1_1_random_access_n_d_image_view_concept.html[RandomAccessNDImageViewConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_random_access_n_d_image_view_concept.html[MutableRandomAccessNDImageViewConcept] -* link:../reference/structboost_1_1gil_1_1_random_access2_d_image_view_concept.html[RandomAccess2DImageViewConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_random_access2_d_image_view_concept.html[MutableRandomAccess2DImageViewConcept] -* link:../reference/structboost_1_1gil_1_1_image_view_concept.html[ImageViewConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_image_view_concept.html[MutableImageViewConcept] -* link:../reference/structboost_1_1gil_1_1_views_compatible_concept.html[ViewsCompatibleConcept] +* xref:reference:boost/gil/RandomAccessNDImageViewConcept.adoc[RandomAccessNDImageViewConcept] +* xref:reference:boost/gil/MutableRandomAccessNDImageViewConcept.adoc[MutableRandomAccessNDImageViewConcept] +* xref:reference:boost/gil/RandomAccess2DImageViewConcept.adoc[RandomAccess2DImageViewConcept] +* xref:reference:boost/gil/MutableRandomAccess2DImageViewConcept.adoc[MutableRandomAccess2DImageViewConcept] +* xref:reference:boost/gil/ImageViewConcept.adoc[ImageViewConcept] +* xref:reference:boost/gil/MutableImageViewConcept.adoc[MutableImageViewConcept] +* xref:reference:boost/gil/ViewsCompatibleConcept.adoc[ViewsCompatibleConcept] ==== == Models diff --git a/doc/modules/ROOT/pages/design/pixel.adoc b/doc/modules/ROOT/pages/design/pixel.adoc index 60067a44c9..e7f4b92b00 100644 --- a/doc/modules/ROOT/pages/design/pixel.adoc +++ b/doc/modules/ROOT/pages/design/pixel.adoc @@ -118,15 +118,15 @@ both, but only pixel values model the latter. ==== See also: -* link:../reference/structboost_1_1gil_1_1_pixel_based_concept.html[PixelBasedConcept

] -* link:../reference/structboost_1_1gil_1_1_pixel_concept.html[PixelConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_pixel_concept.html[MutablePixelConcept] -* link:../reference/structboost_1_1gil_1_1_pixel_value_concept.html[PixelValueConcept] -* link:../reference/structboost_1_1gil_1_1_homogeneous_pixel_based_concept.html[HomogeneousPixelConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_homogeneous_pixel_concept.html[MutableHomogeneousPixelConcept] -* link:../reference/structboost_1_1gil_1_1_homogeneous_pixel_value_concept.html[HomogeneousPixelValueConcept] -* link:../reference/structboost_1_1gil_1_1_pixels_compatible_concept.html[PixelsCompatibleConcept] -* link:../reference/structboost_1_1gil_1_1_pixel_convertible_concept.html[PixelConvertibleConcept] +* xref:reference:boost/gil/PixelBasedConcept.adoc[PixelBasedConcept

] +* xref:reference:boost/gil/PixelConcept.adoc[PixelConcept] +* xref:reference:boost/gil/MutablePixelConcept.adoc[MutablePixelConcept] +* xref:reference:boost/gil/PixelValueConcept.adoc[PixelValueConcept] +* xref:reference:boost/gil/HomogeneousPixelConcept.adoc[HomogeneousPixelConcept] +* xref:reference:boost/gil/MutableHomogeneousPixelConcept.adoc[MutableHomogeneousPixelConcept] +* xref:reference:boost/gil/HomogeneousPixelValueConcept.adoc[HomogeneousPixelValueConcept] +* xref:reference:boost/gil/PixelsCompatibleConcept.adoc[PixelsCompatibleConcept] +* xref:reference:boost/gil/PixelConvertibleConcept.adoc[PixelConvertibleConcept] ==== == Models diff --git a/doc/modules/ROOT/pages/design/pixel_iterator.adoc b/doc/modules/ROOT/pages/design/pixel_iterator.adoc index a2c90b74a0..1e9fc628d6 100644 --- a/doc/modules/ROOT/pages/design/pixel_iterator.adoc +++ b/doc/modules/ROOT/pages/design/pixel_iterator.adoc @@ -32,8 +32,8 @@ concept MutablePixelIteratorConcept : PixelIteratorConcept, MutableRan ==== See also: -* link:../reference/group___pixel_iterator_concept_pixel_iterator.html[PixelIteratorConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_pixel_iterator_concept.html[MutablePixelIteratorConcept] +* xref:reference:boost/gil/PixelIteratorConcept.adoc[PixelIteratorConcept] +* xref:reference:boost/gil/MutablePixelIteratorConcept.adoc[MutablePixelIteratorConcept] ==== === Models @@ -120,8 +120,8 @@ concept MutableIteratorAdaptorConcept : IteratorAdaptorConcept {}; ==== See also: -* link:../reference/structboost_1_1gil_1_1_iterator_adaptor_concept.html[IteratorAdaptorConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_iterator_adaptor_concept.html[MutableIteratorAdaptorConcept] +* xref:reference:boost/gil/IteratorAdaptorConcept.adoc[IteratorAdaptorConcept] +* xref:reference:boost/gil/MutableIteratorAdaptorConcept.adoc[MutableIteratorAdaptorConcept] ==== === Models @@ -266,10 +266,10 @@ support `HasDynamicXStepTypeConcept`. ==== See also: -* link:../reference/structboost_1_1gil_1_1_step_iterator_concept.html[StepIteratorConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_step_iterator_concept.html[MutableStepIteratorConcept] -* link:../reference/structboost_1_1gil_1_1_memory_based_iterator_concept.html[MemoryBasedIteratorConcept] -* link:../reference/structboost_1_1gil_1_1_has_dynamic_x_step_type_concept.html[HasDynamicXStepTypeConcept] +* xref:reference:boost/gil/StepIteratorConcept.adoc[StepIteratorConcept] +* xref:reference:boost/gil/MutableStepIteratorConcept.adoc[MutableStepIteratorConcept] +* xref:reference:boost/gil/MemoryBasedIteratorConcept.adoc[MemoryBasedIteratorConcept] +* xref:reference:boost/gil/HasDynamicXStepTypeConcept.adoc[HasDynamicXStepTypeConcept] ==== === Models diff --git a/doc/modules/ROOT/pages/design/pixel_locator.adoc b/doc/modules/ROOT/pages/design/pixel_locator.adoc index 458984fded..8916a4f632 100644 --- a/doc/modules/ROOT/pages/design/pixel_locator.adoc +++ b/doc/modules/ROOT/pages/design/pixel_locator.adoc @@ -160,14 +160,14 @@ concept MutablePixelLocatorConcept : MutableRandomAcces ==== See also: -* link:../reference/structboost_1_1gil_1_1_has_dynamic_y_step_type_concept.html[HasDynamicYStepTypeConcept] -* link:../reference/structboost_1_1gil_1_1_has_transposed_type_concept.html[HasTransposedTypeConcept] -* link:../reference/structboost_1_1gil_1_1_random_access_n_d_locator_concept.html[RandomAccessNDLocatorConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_random_access_n_d_locator_concept.html[MutableRandomAccessNDLocatorConcept] -* link:../reference/structboost_1_1gil_1_1_random_access2_d_locator_concept.html[RandomAccess2DLocatorConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_random_access2_d_locator_concept.html[MutableRandomAccess2DLocatorConcept] -* link:../reference/structboost_1_1gil_1_1_pixel_locator_concept.html[PixelLocatorConcept] -* link:../reference/structboost_1_1gil_1_1_mutable_pixel_locator_concept.html[MutablePixelLocatorConcept] +* xref:reference:boost/gil/HasDynamicYStepTypeConcept.adoc[HasDynamicYStepTypeConcept] +* xref:reference:boost/gil/HasTransposedTypeConcept.adoc[HasTransposedTypeConcept] +* xref:reference:boost/gil/RandomAccessNDLocatorConcept.adoc[RandomAccessNDLocatorConcept] +* xref:reference:boost/gil/MutableRandomAccessNDLocatorConcept.adoc[MutableRandomAccessNDLocatorConcept] +* xref:reference:boost/gil/RandomAccess2DLocatorConcept.adoc[RandomAccess2DLocatorConcept] +* xref:reference:boost/gil/MutableRandomAccess2DLocatorConcept.adoc[MutableRandomAccess2DLocatorConcept] +* xref:reference:boost/gil/PixelLocatorConcept.adoc[PixelLocatorConcept] +* xref:reference:boost/gil/MutablePixelLocatorConcept.adoc[MutablePixelLocatorConcept] ==== == Models diff --git a/doc/modules/ROOT/pages/design/point.adoc b/doc/modules/ROOT/pages/design/point.adoc index f0f8a5c8b3..2f5d7ed47e 100644 --- a/doc/modules/ROOT/pages/design/point.adoc +++ b/doc/modules/ROOT/pages/design/point.adoc @@ -44,8 +44,8 @@ concept Point2DConcept : PointNDConcept ==== See also: -* link:../reference/structboost_1_1gil_1_1_point_n_d_concept.html[PointNDConcept] -* link:../reference/structboost_1_1gil_1_1_point2_d_concept.html[Point2DConcept] +* xref:reference:boost/gil/PointNDConcept.adoc[PointNDConcept] +* xref:reference:boost/gil/Point2DConcept.adoc[Point2DConcept] ==== == Models diff --git a/doc/modules/ROOT/pages/reference.adoc b/doc/modules/ROOT/pages/reference.adoc index f98dcb75b4..fd3dc8fd11 100644 --- a/doc/modules/ROOT/pages/reference.adoc +++ b/doc/modules/ROOT/pages/reference.adoc @@ -11,15 +11,140 @@ = Reference The complete API reference is generated from the header comments with -Doxygen and published alongside this documentation. - -link:reference/index.html[Open the API Reference,window=_blank] - -NOTE: This library's API reference is planned to move to a -https://github.com/cppalliance/mrdocs[MrDocs]-generated, AsciiDoc-native -reference (as https://www.boost.org/doc/libs/master/libs/url/doc/html/url.html[Boost.URL] -and https://www.boost.org/doc/libs/master/libs/redis/doc/html/redis.html[Boost.Redis] -already do), so that API pages can be cross-referenced directly from the rest -of this documentation. That migration is blocked on a MrDocs issue with -GIL's CRTP-based pixel/image/iterator/locator types; see `doc/mrdocs.yml` -for details. +https://github.com/cppalliance/mrdocs[MrDocs]. This page groups the most +commonly used types and concepts by area; browse +xref:reference:boost/gil.adoc[the full `boost::gil` namespace index] for +everything else, including free functions and metafunctions (most of which +are heavily overloaded and don't have a single canonical page). + +[width=100%] +|=== +| *Point* | *Channel* | *Color Space & Layout* + +| +xref:reference:boost/gil/point.adoc[`point`] + +xref:reference:boost/gil/Point2DConcept.adoc[`Point2DConcept`] + +xref:reference:boost/gil/PointNDConcept.adoc[`PointNDConcept`] + +| +xref:reference:boost/gil/ChannelConcept.adoc[`ChannelConcept`] + +xref:reference:boost/gil/ChannelValueConcept.adoc[`ChannelValueConcept`] + +xref:reference:boost/gil/MutableChannelConcept.adoc[`MutableChannelConcept`] + +| +xref:reference:boost/gil/ColorSpaceConcept.adoc[`ColorSpaceConcept`] + +xref:reference:boost/gil/ColorSpacesCompatibleConcept.adoc[`ColorSpacesCompatibleConcept`] + +xref:reference:boost/gil/ChannelMappingConcept.adoc[`ChannelMappingConcept`] + +xref:reference:boost/gil/layout.adoc[`layout`] + +xref:reference:boost/gil/gray_t.adoc[`gray_t`], +xref:reference:boost/gil/rgb_t.adoc[`rgb_t`], +xref:reference:boost/gil/rgba_t.adoc[`rgba_t`], +xref:reference:boost/gil/cmyk_t.adoc[`cmyk_t`] + +| *Color Base* | *PixelBased* | *Pixel* + +| +xref:reference:boost/gil/ColorBaseConcept.adoc[`ColorBaseConcept`] + +xref:reference:boost/gil/HomogeneousColorBaseConcept.adoc[`HomogeneousColorBaseConcept`] + +xref:reference:boost/gil/MutableColorBaseConcept.adoc[`MutableColorBaseConcept`] + +| +xref:reference:boost/gil/PixelBasedConcept.adoc[`PixelBasedConcept`] + +xref:reference:boost/gil/HomogeneousPixelBasedConcept.adoc[`HomogeneousPixelBasedConcept`] + +| +xref:reference:boost/gil/pixel.adoc[`pixel`] + +xref:reference:boost/gil/packed_pixel.adoc[`packed_pixel`] + +xref:reference:boost/gil/bit_aligned_pixel_reference.adoc[`bit_aligned_pixel_reference`] + +xref:reference:boost/gil/planar_pixel_reference.adoc[`planar_pixel_reference`] + +xref:reference:boost/gil/PixelConcept.adoc[`PixelConcept`] + +xref:reference:boost/gil/HomogeneousPixelConcept.adoc[`HomogeneousPixelConcept`] + +xref:reference:boost/gil/MutablePixelConcept.adoc[`MutablePixelConcept`] + +| *Pixel Iterator* | *Pixel Locator* | *Image View* + +| +xref:reference:boost/gil/PixelIteratorConcept.adoc[`PixelIteratorConcept`] + +xref:reference:boost/gil/MutablePixelIteratorConcept.adoc[`MutablePixelIteratorConcept`] + +xref:reference:boost/gil/memory_based_step_iterator.adoc[`memory_based_step_iterator`] + +xref:reference:boost/gil/planar_pixel_iterator.adoc[`planar_pixel_iterator`] + +xref:reference:boost/gil/bit_aligned_pixel_iterator.adoc[`bit_aligned_pixel_iterator`] + +| +xref:reference:boost/gil/PixelLocatorConcept.adoc[`PixelLocatorConcept`] + +xref:reference:boost/gil/RandomAccess2DLocatorConcept.adoc[`RandomAccess2DLocatorConcept`] + +xref:reference:boost/gil/memory_based_2d_locator.adoc[`memory_based_2d_locator`] + +xref:reference:boost/gil/virtual_2d_locator.adoc[`virtual_2d_locator`] + +| +xref:reference:boost/gil/ImageViewConcept.adoc[`ImageViewConcept`] + +xref:reference:boost/gil/RandomAccess2DImageViewConcept.adoc[`RandomAccess2DImageViewConcept`] + +xref:reference:boost/gil/image_view.adoc[`image_view`] + +| *Image* | *Dynamic Image (Variant)* | *Toolbox* + +| +xref:reference:boost/gil/ImageConcept.adoc[`ImageConcept`] + +xref:reference:boost/gil/image.adoc[`image`] + +| +xref:reference:boost/gil/any_image.adoc[`any_image`] + +xref:reference:boost/gil/any_image_view.adoc[`any_image_view`] + +| +xref:reference:boost/gil/indexed_image.adoc[`indexed_image`] + +| *Numeric* | *IO* | + +| +xref:reference:boost/gil/matrix3x2.adoc[`matrix3x2`] + +xref:reference:boost/gil/kernel_1d.adoc[`kernel_1d`] + +xref:reference:boost/gil/kernel_1d_fixed.adoc[`kernel_1d_fixed`] + +| +xref:reference:boost/gil/image_read_settings.adoc[`image_read_settings`] + +xref:reference:boost/gil/image_write_info.adoc[`image_write_info`] + +xref:reference:boost/gil/format_tag.adoc[`format_tag`] + +xref:reference:boost/gil/reader_backend.adoc[`reader_backend`] + +| +|=== + +NOTE: The `extension/io/{jpeg,png,tiff,bmp,pnm,raw}` format backends aren't +in this reference yet -- MrDocs needs the corresponding image library +(libjpeg, libpng, libtiff, ...) just to parse those headers, and the doc +build doesn't pull those in. See `doc/mrdocs-src/all.cpp` for the exact +scope. diff --git a/doc/mrdocs-src/all.cpp b/doc/mrdocs-src/all.cpp new file mode 100644 index 0000000000..417935ca1c --- /dev/null +++ b/doc/mrdocs-src/all.cpp @@ -0,0 +1,45 @@ +// +// Copyright (c) 2026 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) +// +// Official repository: https://github.com/boostorg/gil +// +// Single translation unit used by MrDocs (see ../mrdocs.yml) to extract GIL's +// public API reference. Deliberately excludes extension/io/{jpeg,png,tiff,...} +// (the format-specific backends), since those require the corresponding +// external image libraries (libjpeg, libpng, libtiff, ...) just to parse. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index df05bd2538..fe4f419785 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -6,32 +6,30 @@ # # Official repository: https://github.com/boostorg/gil # -# NOT YET WIRED IN. This is a best-effort MrDocs config prepared for when -# https://github.com/cppalliance/mrdocs handles GIL's headers without crashing. -# As of MrDocs 0.8.0, most of GIL's core headers (anything using the CRTP -# proxy-reference pattern: pixel, image, image_view, locator, the pixel -# iterators, algorithm.hpp, rgb.hpp, and all of image_processing/) segfault -# the tool in isolation -- this is not a config problem. Once that's fixed -# upstream, add to antora.yml: -# ext: -# cpp-reference: -# config: doc/mrdocs.yml -# and replace modules/ROOT/pages/reference.adoc's link to the Doxygen HTML -# with xref:reference:boost/gil/.adoc[] links, as boostorg/redis and -# boostorg/url do. +# Wired into antora.yml as ext: cpp-reference: config: doc/mrdocs.yml. +# +# GIL's extension/io/{jpeg,png,tiff,bmp,pnm,raw} backends need the matching +# external image libraries just to parse, so mrdocs-src/all.cpp deliberately +# doesn't include them; everything else (core + concepts + image_processing + +# the dynamic_image/toolbox/numeric/histogram extensions + the generic io +# layer) is covered. `exclude` below is belt-and-suspenders: those symbols +# are never reachable from mrdocs-src/all.cpp anyway. +# +# `compilation-database` is generated by build_antora.sh (gitignored) since +# it needs the resolved $BOOST_SRC_DIR baked into its include path. The file +# must be literally named `compile_commands.json` -- MrDocs 0.8.0+47bdd690824e +# silently rejects any other basename with "Invalid compilation database +# path", even given as an absolute path to a file that exists and parses. source-root: .. - -# Same subdirectories doc/doxyfile currently lists (RECURSIVE = NO there too). input: - ../include/boost/gil - - ../include/boost/gil/concepts - - ../include/boost/gil/image_processing - - ../include/boost/gil/io - - ../include/boost/gil/extension/io - - ../include/boost/gil/extension/dynamic_image +recursive: true file-patterns: - '*.hpp' +exclude: + - ../include/boost/gil/extension/io +compilation-database: compile_commands.json include-symbols: - 'boost::gil::**' @@ -42,9 +40,3 @@ exclude-symbols: generator: adoc multipage: true base-url: https://github.com/boostorg/gil/blob/develop/ - -# GIL is header-only; this single translation unit includes everything the -# `input` filter above will look for, following the pattern documented in -# boostorg/url's mrdocs.yml (a single TU is much faster than a full project -# build and MrDocs only needs compile flags, not a real link step). -cmake: '-D BOOST_GIL_BUILD_EXAMPLES=OFF -D BOOST_GIL_BUILD_HEADER_TESTS=OFF -D BUILD_TESTING=OFF' diff --git a/doc/package-lock.json b/doc/package-lock.json index ad527b2e66..bbfd2337e9 100644 --- a/doc/package-lock.json +++ b/doc/package-lock.json @@ -4,6 +4,9 @@ "requires": true, "packages": { "": { + "dependencies": { + "@cppalliance/antora-cpp-reference-extension": "^0.1.1" + }, "devDependencies": { "@antora/cli": "^3.1.15", "@antora/site-generator": "^3.1.15", @@ -104,7 +107,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@antora/expand-path-helper/-/expand-path-helper-3.0.0.tgz", "integrity": "sha512-7PdEIhk97v85/CSm3HynCsX14TR6oIVz1s233nNLsiWubE8tTnpPt4sNRJR+hpmIZ6Bx9c6QDp3XIoiyu/WYYA==", - "dev": true, "license": "MPL-2.0", "engines": { "node": ">=16.0.0" @@ -302,6 +304,43 @@ "yarn": ">=1.1.0" } }, + "node_modules/@cppalliance/antora-cpp-reference-extension": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@cppalliance/antora-cpp-reference-extension/-/antora-cpp-reference-extension-0.1.1.tgz", + "integrity": "sha512-otGW/pJNtYyji62ELGkyEc6PseHxz882HhQGB1YUxPehg+2CjzM/hqQ7aJFXtaNAedrXK0/upQ+KCCs2WPWFqg==", + "license": "BSL-1.0", + "dependencies": { + "@antora/expand-path-helper": "^3.0.0", + "axios": "^1.16.1", + "cache-directory": "^2.0.0", + "fast-glob": "^3.3.3", + "isomorphic-git": "^1.38.1", + "js-yaml": "^4.1.1", + "semver": "^7.8.1" + } + }, + "node_modules/@cppalliance/antora-cpp-reference-extension/node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -313,7 +352,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -327,7 +365,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -337,7 +374,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -358,7 +394,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -367,6 +402,18 @@ "node": ">=6.5" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/antora": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/antora/-/antora-3.2.0.tgz", @@ -388,7 +435,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/asciidoctor-opal-runtime": { @@ -409,7 +455,12 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", - "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, "node_modules/atomic-sleep": { @@ -426,7 +477,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -438,6 +488,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, "node_modules/b4a": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", @@ -472,7 +534,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -493,7 +554,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -506,7 +566,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, "funding": [ { "type": "github", @@ -541,7 +600,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/cache-directory/-/cache-directory-2.0.0.tgz", "integrity": "sha512-7YKEapH+2Uikde8hySyfobXBqPKULDyHNl/lhKm7cKf/GJFdG/tU/WpLrOg2y9aUrQrWUilYqawFIiGJPS6gDA==", - "dev": true, "license": "LGPL-3.0+", "dependencies": { "xdg-basedir": "^3.0.0" @@ -554,7 +612,6 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -573,7 +630,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -587,7 +643,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -604,7 +659,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", - "dev": true, "license": "Apache-2.0" }, "node_modules/clone": { @@ -617,6 +671,18 @@ "node": ">=0.8" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", @@ -645,7 +711,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, "license": "Apache-2.0", "bin": { "crc32": "bin/crc32.njs" @@ -654,11 +719,27 @@ "node": ">=0.8" } }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" @@ -674,7 +755,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -688,18 +768,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/diff3": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", - "dev": true, "license": "MIT" }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -714,7 +801,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -724,7 +810,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -734,7 +819,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -743,11 +827,25 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -757,7 +855,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.x" @@ -784,7 +881,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -801,7 +897,6 @@ "version": "1.20.3", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -811,7 +906,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -820,11 +914,30 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -836,11 +949,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -850,7 +999,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -875,7 +1023,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -889,7 +1036,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -902,7 +1048,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -937,7 +1082,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -950,7 +1094,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -963,7 +1106,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -979,7 +1121,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -998,11 +1139,23 @@ "node": ">=14" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -1023,7 +1176,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -1033,14 +1185,12 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -1053,7 +1203,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1063,7 +1212,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -1076,7 +1224,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -1086,7 +1233,6 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" @@ -1102,14 +1248,12 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, "license": "MIT" }, "node_modules/isomorphic-git": { "version": "1.38.10", "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.38.10.tgz", "integrity": "sha512-nJSSq7ypu97vM33rSdxXyPzSWksCberjKspzXhFEcBHdVyxdNkWByAzJ/AURV1jIlfv8pLq37lGdIEt7ORj17Q==", - "dev": true, "license": "MIT", "dependencies": { "async-lock": "^1.4.1", @@ -1178,7 +1322,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -1188,7 +1331,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -1198,7 +1340,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -1212,7 +1353,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -1252,7 +1392,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1265,7 +1404,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1275,12 +1413,17 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", - "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.5" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/multi-progress": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/multi-progress/-/multi-progress-4.0.0.tgz", @@ -1312,7 +1455,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -1322,7 +1464,6 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true, "license": "(MIT AND Zlib)" }, "node_modules/pend": { @@ -1349,7 +1490,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -1399,7 +1539,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -1409,7 +1548,6 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -1442,11 +1580,19 @@ "node": ">=0.4.0" } }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -1474,7 +1620,6 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", @@ -1518,7 +1663,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -1529,7 +1673,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -1553,7 +1696,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -1580,11 +1722,22 @@ "node": ">=10" } }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -1602,7 +1755,6 @@ "version": "2.4.12", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", - "dev": true, "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.4", @@ -1630,7 +1782,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, "funding": [ { "type": "github", @@ -1651,7 +1802,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, "funding": [ { "type": "github", @@ -1719,7 +1869,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -1769,7 +1918,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", - "dev": true, "license": "MIT", "dependencies": { "isarray": "^2.0.5", @@ -1784,7 +1932,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -1797,7 +1944,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.3", @@ -1852,7 +1998,6 @@ "version": "1.1.22", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", - "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -1881,14 +2026,12 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/xdg-basedir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", "integrity": "sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" diff --git a/doc/package.json b/doc/package.json index 6643a96f68..9f9e680e77 100644 --- a/doc/package.json +++ b/doc/package.json @@ -3,5 +3,8 @@ "@antora/cli": "^3.1.15", "@antora/site-generator": "^3.1.15", "antora": "^3.1.15" + }, + "dependencies": { + "@cppalliance/antora-cpp-reference-extension": "^0.1.1" } } diff --git a/doc/reference.dox b/doc/reference.dox deleted file mode 100644 index 3c387909b9..0000000000 --- a/doc/reference.dox +++ /dev/null @@ -1,281 +0,0 @@ - /// \defgroup BasicConcepts Basic Concepts - /// \brief Various basic concepts - - /// \defgroup Point Point - /// \brief N-dimensional point - /// \defgroup PointConcept Concepts - /// \ingroup Point - /// \brief Concepts for points - - /// \defgroup PointModel Models - /// \ingroup Point - /// \brief Models for points - - /// \defgroup PointAlgorithm Algorithms and Utility Functions - /// \ingroup Point - /// \brief Algorithms and Utility Functions for points - - /// \defgroup ColorSpaceAndLayout Color, Color Space, and Layout - /// \brief The color space and the layout define the set, ordering and interpretation of channels in a pixel - /// \defgroup ColorSpaceAndLayoutConcept Concepts - /// \ingroup ColorSpaceAndLayout - - /// \defgroup ColorSpaceAndLayoutModel Models - /// \ingroup ColorSpaceAndLayout - - /// \defgroup ColorNameModel Color Names - /// \ingroup ColorSpaceAndLayoutModel - - /// \defgroup ColorSpaceModel Color Spaces - /// \ingroup ColorSpaceAndLayoutModel - - /// \defgroup LayoutModel Layouts - /// \ingroup ColorSpaceAndLayoutModel - - /// \defgroup Channel Channel - /// \brief Channel is the building block of pixel - /// \defgroup ChannelConcept Concepts - /// \ingroup Channel - /// \brief Concepts for channels - - /// \defgroup ChannelModel Models - /// \ingroup Channel - /// \brief Channel models. Although not required by the ChannelConcept, all GIL-provided channels support arithmetic operations - - /// \defgroup ChannelAlgorithm Algorithms and Utility Functions - /// \ingroup Channel - /// \brief Channel algorithms, metafunctions and utility functions - -/** - \defgroup ColorBase ColorBase - \brief A color base is a container of color elements. - -The most common use of color base is in the implementation of a pixel, in which case the color -elements are channel values. The color base concept, however, can be used in other scenarios. For example, a planar pixel has channels that are not -contiguous in memory. Its reference is a proxy class that uses a color base whose elements are channel references. Its iterator uses a color base -whose elements are channel iterators. -*/ - /// \defgroup ColorBaseConcept Concepts - /// \ingroup ColorBase - /// \brief ColorBase concepts - - /// \defgroup ColorBaseModel Models - /// \ingroup ColorBase - /// \brief ColorBase models - - /// \defgroup ColorBaseAlgorithm Algorithms and Utility Functions - /// \ingroup ColorBase - /// \brief ColorBase algorithms, metafunctions and utility functions - -/** - \defgroup PixelBased PixelBased - \brief Concepts for all GIL constructs that are pixel-based (pixels, pixel iterators, locators, views, images). - - PixelBasedConcept provides a uniform interface for getting some common properties of pixel-based constructs, such as the number of channels, - the color space, the layout, etc. -*/ - /// \defgroup PixelBasedConcept Concepts - /// \ingroup PixelBased - /// \brief PixelBased concepts - - /// \defgroup PixelBasedModel Models - /// \ingroup PixelBased - /// \brief PixelBased models - - /// \defgroup PixelBasedAlgorithm Algorithms and Utility Functions - /// \ingroup PixelBased - /// \brief PixelBased algorithms, metafunctions and utility functions. - -/** - \defgroup Pixel Pixel - \brief A pixel is a set of channels defining the color at a given point in an image. - -Conceptually, a pixel is little more than a color base whose elements model \p ChannelConcept. -Many properties of pixels inherit from color bases: pixels may be homogeneous if all of their channels have the same type; otherwise they are -called heterogeneous. The channels of a pixel may be addressed using semantic or physical indexing, or by color; all color base algorithms -work on pixels as well. Two pixels are compatible if their color spaces are the same and their channels, paired semantically, are compatible. -Note that constness, memory organization and reference/value are ignored. For example, an 8-bit RGB planar reference is compatible to a constant 8-bit -BGR interleaved pixel value. Most pairwise pixel operations (copy construction, assignment, equality, etc.) are only defined for compatible pixels. - -*/ - /// \defgroup PixelConcept Concepts - /// \ingroup Pixel - /// \brief Pixel concepts - - /// \defgroup PixelModel Models - /// \ingroup Pixel - /// \brief Pixel models - -/** - \defgroup PixelAlgorithm Algorithms and Utility Functions - \ingroup Pixel - \brief Pixel algorithms, metafunctions and utility functions. - -Since pixels model the ColorBaseConcept, all color-base related algorithms also apply to pixels. See \ref ColorBaseAlgorithm for more. - -*/ - - /// \defgroup PixelDereferenceAdaptor Pixel Dereference Adaptor - /// \brief A function object invoked upon accessing of the pixel of a pixel iterator/locator/view - - /// \defgroup PixelDereferenceAdaptorConcept Concepts - /// \ingroup PixelDereferenceAdaptor - - /// \defgroup PixelDereferenceAdaptorModel Models - /// \ingroup PixelDereferenceAdaptor - - - /// \defgroup PixelIterator Pixel Iterator - /// \brief STL Iterators over models of PixelConcept - - /// \defgroup PixelIteratorConcept Concepts - /// \ingroup PixelIterator - /// \brief Pixel iterator concepts - - /// \defgroup PixelIteratorModel Models - /// \ingroup PixelIterator - /// \brief Pixel iterator models - - - /// \defgroup PixelLocator Pixel Locator - /// \brief Generalization of an iterator to multiple dimensions - - /// \defgroup PixelLocatorConcept Concepts - /// \ingroup PixelLocator - /// \brief Pixel locator concepts - - /// \defgroup PixelLocatorModel Models - /// \ingroup PixelLocator - /// \brief Pixel locator models - - /// \defgroup ImageView Image View - /// \brief N-dimensional range - - /// \defgroup ImageViewConcept Concepts - /// \ingroup ImageView - /// \brief Image view concepts - - /// \defgroup ImageViewModel Models - /// \ingroup ImageView - /// \brief Image view models - - /// \defgroup ImageViewAlgorithm Algorithms and Utility Functions - /// \ingroup ImageView - /// \brief Image view algorithms, metafunctions and utility functions - -/** - \defgroup Image Image - \brief N-dimensional container - - An image is a container of N-dimensional data. GIL provides only one model, a two dimensional image whose \p value_type is a pixel. - - Images are regular types (which means they have a default constructor, a copy constructor, \p operator=, \p operator==, \p operator!=, and \p swap) - As containers, images own the data, which means they allocate the data in their constructors and deallocate in the destructors. Their copy construction, - assignment and equality comparison is deep (i.e. propagates the operation to the values). That makes images expensive to pass by value, unlike views. - - Also, unlike views, images propagate their constness to the data. An const-qualified image does not allow for modifying its pixels and does not provide - a mutable view over its pixels. - - Images provide two services: they manage ownership of their data (the pixels) and they can return a view over their pixels. - Algorithms predominantly operate on views. This is analogous to the STL: In the STL containers (like \p std::vector) provide ranges (\p vec.begin() - and \p vec.end() ) and algorithms typically operate on ranges. The GIL equivalent of a range is an image view. - -*/ - /// \defgroup ImageConcept Concepts - /// \ingroup Image - /// \brief Image concepts - - /// \defgroup ImageModel Models - /// \ingroup Image - /// \brief Image models - -/** - \defgroup ImageProcessing Image Processing - \brief Image Processing algorithms - - Collection of image processing algorithms currently implemented by GIL. -*/ - - /// \defgroup Variant Variant - /// \brief A holder of a runtime instantiated type. Used to provide runtime-specified images and views - - /// \defgroup Metafunctions Metafunctions - /// \brief Metafunctions to construct or query GIL types - /// \defgroup TypeFactory Type Factory Metafunctions - /// \ingroup Metafunctions - /// \brief Metafunctions that construct GIL types from related types or from components - - /// \defgroup TypeAnalysis Type Analysis Metafunctions - /// \ingroup Metafunctions - /// \brief Metafunctions that determine properties of GIL types - - /// \defgroup IO I/O - /// \brief Support for reading and writing images to file - /// \defgroup JPEG_IO JPEG I/O - /// \ingroup IO - /// \brief Support for reading and writing JPEG image files - - /// \defgroup TIFF_IO TIFF I/O - /// \ingroup IO - /// \brief Support for reading and writing TIFF image files - - /// \defgroup PNG_IO PNG I/O - /// \ingroup IO - /// \brief Support for reading and writing PNG image files - -/*! -\mainpage API Reference - -\section Modules - - - \ref BasicConcepts - - \ref Point - - \ref PointConcept - - \ref PointModel - - \ref PointAlgorithm - - \ref ColorSpaceAndLayout - - \ref ColorSpaceAndLayoutConcept - - \ref ColorSpaceAndLayoutModel - - \ref Channel - - \ref ChannelConcept - - \ref ChannelModel - - \ref ChannelAlgorithm - - \ref ColorBase - - \ref ColorBaseConcept - - \ref ColorBaseModel - - \ref ColorBaseAlgorithm - - \ref PixelBased - - \ref PixelBasedConcept - - \ref PixelBasedModel - - \ref PixelBasedAlgorithm - - \ref Pixel - - \ref PixelConcept - - \ref PixelModel - - \ref PixelAlgorithm - - \ref PixelDereferenceAdaptor - - \ref PixelDereferenceAdaptorConcept - - \ref PixelDereferenceAdaptorModel - - \ref PixelIterator - - \ref PixelIteratorConcept - - \ref PixelIteratorModel - - \ref PixelLocator - - \ref PixelLocatorConcept - - \ref PixelLocatorModel - - \ref ImageView - - \ref ImageViewConcept - - \ref ImageViewModel - - \ref ImageViewAlgorithm - - \ref Image - - \ref ImageConcept - - \ref ImageModel - - \ref ImageProcessing - - \ref Metafunctions - - \ref TypeFactory - - \ref TypeAnalysis - - \ref Variant - - \ref IO - - \ref JPEG_IO - - \ref TIFF_IO - - \ref PNG_IO -*/ - From 259d2a5b416123d6891c6ed190097e513cc3f475 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 12:17:56 +0200 Subject: [PATCH 21/37] ci: fix docs build running from a non-git copy of the checkout setup-boost's `cp -r $GITHUB_WORKSPACE/* libs/gil` (into ../boost-root) drops .git (cp -r with a glob doesn't match dotfiles), but local-playbook.yml's Antora content source (`url: ..`) requires a real git repository. Found via the docs-ci-smoke test branch: FATAL (antora): Local content source must be a git repository: /home/runner/work/gil/boost-root/libs/gil (url: ..) Run the build from the original checkout instead (which does have .git), and only use ../boost-root for BOOST_SRC_DIR (MrDocs' cross-library header resolution), matching how boostorg/url and boostorg/redis's CI scripts keep those two concerns separate. Co-Authored-By: Claude Sonnet 5 --- .github/actions/generate-doc/action.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/actions/generate-doc/action.yml b/.github/actions/generate-doc/action.yml index 719aad8c0b..dff8544db2 100644 --- a/.github/actions/generate-doc/action.yml +++ b/.github/actions/generate-doc/action.yml @@ -4,7 +4,14 @@ runs: using: composite steps: - run: | - cd ../boost-root/libs/gil/doc + # Antora's local content source (doc/local-playbook.yml: `url: ..`) + # must be a real git repository. setup-boost's copy into + # ../boost-root/libs/gil (via `cp -r`) drops .git, so the build must + # run from the original checkout ($GITHUB_WORKSPACE) -- only + # BOOST_SRC_DIR (cross-library headers for MrDocs) comes from + # ../boost-root. + export BOOST_SRC_DIR="$(cd ../boost-root && pwd)" + cd doc bash build_antora.sh cd .. chmod +x $GITHUB_WORKSPACE/.github/actions/generate-doc/docs-config.sh From b1bf2285c441b31397522314103fc338d3604e85 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 13:03:26 +0200 Subject: [PATCH 22/37] docs: add pixi tooling, validated via the docs-ci-smoke test branch Add libs/gil/pixi.toml -- a self-contained `docs` environment (Node, a C++ compiler, and MrDocs from the sdebionne/label/esrf-bcu channel, which repackages a fixed MrDocs dev build until the upstream segfault fix ships in an official release) -- so `pixi run -e docs docs-build` is a fully self-contained one-command local build. build_antora.sh now auto-detects a pixi/conda-provided MrDocs via CONDA_PREFIX when MRDOCS_ROOT isn't already set, instead of falling through to the antora-cpp-reference-extension's auto-download (which currently fetches the still-broken official release). Co-Authored-By: Claude Sonnet 5 --- doc/build_antora.sh | 13 ++ pixi.lock | 381 ++++++++++++++++++++++++++++++++++++++++++++ pixi.toml | 30 ++++ 3 files changed, 424 insertions(+) create mode 100644 pixi.lock create mode 100644 pixi.toml diff --git a/doc/build_antora.sh b/doc/build_antora.sh index 8fd11624d4..6d9c8c4568 100644 --- a/doc/build_antora.sh +++ b/doc/build_antora.sh @@ -26,6 +26,19 @@ fi export BOOST_SRC_DIR="${BOOST_SRC_DIR:-$(realpath "$SCRIPT_DIR/../../..")}" echo "Using BOOST_SRC_DIR=$BOOST_SRC_DIR" +# Pin the @cppalliance/antora-cpp-reference-extension to a local MrDocs +# instead of it downloading the latest GitHub release, which as of this +# writing still segfaults on GIL's headers (upstream fix not released yet). +# When running under `pixi run -e docs`, CONDA_PREFIX/bin/mrdocs is the +# package from the sdebionne/label/esrf-bcu channel (see pixi.toml), which +# repackages the fixed dev build. Falls through to auto-download otherwise. +if [ -z "${MRDOCS_ROOT:-}" ] && [ -n "${CONDA_PREFIX:-}" ] && [ -x "$CONDA_PREFIX/bin/mrdocs" ]; then + export MRDOCS_ROOT="$CONDA_PREFIX" +fi +if [ -n "${MRDOCS_ROOT:-}" ]; then + echo "Using MRDOCS_ROOT=$MRDOCS_ROOT" +fi + echo "Generating the MrDocs compilation database..." # See mrdocs.yml / mrdocs-src/all.cpp: a single translation unit is enough # for MrDocs to extract the whole public API, and is far faster than trying diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 0000000000..ba1c3e5aea --- /dev/null +++ b/pixi.lock @@ -0,0 +1,381 @@ +version: 7 +platforms: +- name: linux-64 + virtual-packages: + - __unix=0=0 + - __linux=4.18 + - __glibc=2.28 + - __archspec=0=x86_64 +environments: + default: + channels: + - url: https://conda.anaconda.org/sdebionne/label/esrf-bcu/ + - url: https://conda.anaconda.org/conda-forge/ + packages: {} + docs: + channels: + - url: https://conda.anaconda.org/sdebionne/label/esrf-bcu/ + - url: https://conda.anaconda.org/conda-forge/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-15.3.0-h8846f6e_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-2.0.0-hdab9b82_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-15.3.0-hc6a0c74_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-15.3.0-h6f13dc8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-15.3.0-hc6a0c74_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-15.3.0-h90d9265_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-15.3.0-h54950a5_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.20.2-h4dda4d0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.3.0-h2b852eb_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.3.0-hb2c5482_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/sdebionne/label/esrf-bcu/linux-64/mrdocs-0.8.0-hb0f4dca_0.conda +packages: +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + build_number: 20 + sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 + md5: a9f577daf3de00bca7c3c76c0ecbd1de + depends: + - __glibc >=2.17,<3.0.a0 + - libgomp >=7.5.0 + constrains: + - openmp_impl <0.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + strong: + - _openmp_mutex >=4.5 + size: 28948 + timestamp: 1770939786096 +- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_102.conda + sha256: fb7bf36984a37ce7e4714d1d1da0bd0e3bfc679520f5cdc184afc676fd4b5da2 + md5: a0c5e0b7f58c8ceeb08e5bc41251d5a2 + depends: + - ld_impl_linux-64 2.46.1 default_hbd61a6d_102 + - sysroot_linux-64 + - zstd >=1.5.7,<1.6.0a0 + license: GPL-3.0-only + license_family: GPL + run_exports: {} + size: 3713752 + timestamp: 1784214522814 +- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-15.3.0-h8846f6e_4.conda + sha256: 87ebd4f94c8823763d35adaba292c2dc8c60ddda70e0980f32eae0dbb0e2aa2d + md5: 4bfbca2f3bfcdf6d78a339cfd5368fce + depends: + - gcc_impl_linux-64 >=15.3.0,<15.3.1.0a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 32631 + timestamp: 1787618595567 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-2.0.0-hdab9b82_0.conda + sha256: 384ab445d260e0d69e701c32eb0ed870d348ec7e3efc78d944823855d4512ca1 + md5: 923dacbfd97122c65c97a35f6e959ab8 + depends: + - gxx 15.* + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 6972 + timestamp: 1786642183694 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-15.3.0-hc6a0c74_4.conda + sha256: f9ee593ac1cbad6633c51c498b0ba8b1da14e6dc22c0cb49a1d158abe8d2cb0f + md5: cd390c3b900677ec6b0fdd729173125d + depends: + - conda-gcc-specs + - gcc_impl_linux-64 15.3.0 h6f13dc8_4 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 29431 + timestamp: 1787618696046 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-15.3.0-h6f13dc8_4.conda + sha256: c7e4915c0c5e8f081e107d50ab6041eab17fbe094108538d614faed354ab8974 + md5: b1f9f0b4a4697fd26c1fe3c7a1ac659f + depends: + - binutils_impl_linux-64 >=2.46.1 + - libgcc >=15.3.0 + - libgcc-devel_linux-64 15.3.0 h2b852eb_104 + - libgomp >=15.3.0 + - libsanitizer 15.3.0 h54950a5_4 + - libstdcxx >=15.3.0 + - libstdcxx-devel_linux-64 15.3.0 hb2c5482_104 + - sysroot_linux-64 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 82843060 + timestamp: 1787618483258 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-15.3.0-hc6a0c74_4.conda + sha256: 9ea6ca0830d8fe8ede308b4ab3f206f3504c422f7155981e13f2eca56a5ef9e8 + md5: 2264c7beb16d977423e8ebd69f44be88 + depends: + - conda-gcc-specs + - gcc 15.3.0 hc6a0c74_4 + - gxx_impl_linux-64 15.3.0 h90d9265_4 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 28893 + timestamp: 1787618735531 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-15.3.0-h90d9265_4.conda + sha256: 2b18349ad16f4be8dd9f2afa58d4ab7b9014fc543326ddecd59db215c6dc721d + md5: 9a6fbf3da4dd624b8382e096bbca10f4 + depends: + - gcc_impl_linux-64 15.3.0 h6f13dc8_4 + - libstdcxx-devel_linux-64 15.3.0 hb2c5482_104 + - sysroot_linux-64 + - tzdata + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 16252239 + timestamp: 1787618666649 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + sha256: 9f07834f0c546ab14d885ce0366285f61f44e326c0edd1fc63b8294e113ae432 + md5: 72a381cbad04f24b1c2a43ef707f45b4 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - icu >=78.3,<79.0a0 + size: 14459115 + timestamp: 1786545741408 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + sha256: 27d83f1188cd19bcb7754a078b3fa7f4cfb8527f8eb2fde54dd01fc529d1adec + md5: 449500f2c089da11c40f5c21312e3e07 + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.46.1 + license: GPL-3.0-only + license_family: GPL + run_exports: {} + size: 745303 + timestamp: 1784214507189 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda + sha256: 24090e675d34403b4ee1cd4372d8f6c0937da7ecfd66a19a57cac2ed0f4ea793 + md5: cba14d01083fc62ffd32c24d7d390633 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==16.2.0=*_4 + - libgomp 16.2.0 he0feb66_4 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 1058083 + timestamp: 1787618680111 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda + sha256: 0fe5cb8e0752241ab55e11656ed1b9726248b522d23b929fe7c95b83eb55b9bb + md5: 89d2c1231f47bd818f5d624b9411459d + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: + strong: + - _openmp_mutex >=4.5 + size: 639968 + timestamp: 1787618616266 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-15.3.0-h54950a5_4.conda + sha256: 074869ef41874beec0ffee576fdd809f72eb8a4196fe2fa4d1a922c9a0742572 + md5: 306fcd92c8f1cd7049bc6e361bd37f09 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15.3.0 + - libstdcxx >=15.3.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: + weak: + - libsanitizer 15.3.0 + size: 7579092 + timestamp: 1787618435127 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda + sha256: 40b792b0186c1e8859280a1f6f19a54fc50a11b32724fc7b637009c1a9bd302b + md5: 2f2ef0d96de5bdd8c1270ff22fdf9352 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 16.2.0 ha9f2e26_4 + constrains: + - libstdcxx-ng ==16.2.0=*_4 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 6613148 + timestamp: 1787618704262 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda + sha256: 67761d0206f84140047a367eaf9befe03a7e157a29ee25aee0047b40801cc6b6 + md5: 01c4ed87826af55996768af6dbc936b4 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libuv >=1.52.1,<2.0a0 + size: 420040 + timestamp: 1785914567661 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 + md5: 0de0122d9570a8ab637c6b73db268389 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 63713 + timestamp: 1785362952714 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.20.2-h4dda4d0_2.conda + sha256: a3e0b1dd02b24e8959ab11f0f72c01fcbaea47d12a3d60b3289c8766da182fed + md5: d49c2e5d7f0963290033007ccc2c69bb + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libstdcxx >=15 + - libuv >=1.52.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.8,<4.0a0 + - zlib + license: MIT + license_family: MIT + run_exports: + weak: + - nodejs >=20.20.2,<21.0a0 + size: 17509809 + timestamp: 1788202854345 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + sha256: 4747b2d6a8336f52343bceb8a1ebf41a9e6e665b9d2e3f989972de53a310599e + md5: 16e3034a330cc625f2c685edec60cf4e + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=15 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - openssl >=3.6.4,<4.0a0 + size: 3202955 + timestamp: 1787698780103 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda + sha256: 16080a1c7724f7d25727cdc23c7658e0cec2db52448c1dc0c33467ee2c6e1c62 + md5: 6acb86426229f96f93e5468d1df3a5e8 + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib 1.3.2 h25fd6f3_3 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 96132 + timestamp: 1785362957588 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + sha256: 47d682b9f6d6ec9eb1a6e6c3e75ea6273e899e78fb7fc59f81d39745009fbc60 + md5: aa459086047c0e5e27023ab19f8cb86a + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - zstd >=1.5.7,<1.6.0a0 + size: 601301 + timestamp: 1786599621503 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + sha256: 0a0544cf95f64394fe4959286f5c71f5444ad58feb0602e53becb27448d24da6 + md5: 0f51e2391ade309db462a55611263e9c + depends: + - __unix + license: ISC + run_exports: {} + size: 131780 + timestamp: 1784754889428 +- conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda + sha256: 41557eeadf641de6aeae49486cef30d02a6912d8da98585d687894afd65b356a + md5: 86d9cba083cd041bfbf242a01a7a1999 + constrains: + - sysroot_linux-64 ==2.28 + license: LGPL-2.0-or-later AND LGPL-2.0-or-later WITH exceptions AND GPL-2.0-or-later + license_family: GPL + run_exports: {} + size: 1278712 + timestamp: 1765578681495 +- conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.3.0-h2b852eb_104.conda + sha256: 83221295f52a5ac410f092b4fe1415052b8542f76ddf9ee9949a3571d19e1d43 + md5: b206066f9a3900bf869ac1a964686c9c + depends: + - __unix + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 3093906 + timestamp: 1787618317103 +- conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.3.0-hb2c5482_104.conda + sha256: 42de47979a998f594c378498ebdf2f1c833e6a4611dd2ddda56f1ba2d1515c7f + md5: c428c727fc8697f18a140c676032f8f5 + depends: + - __unix + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 20671212 + timestamp: 1787618340216 +- conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + sha256: c47299fe37aebb0fcf674b3be588e67e4afb86225be4b0d452c7eb75c086b851 + md5: 13dc3adbc692664cd3beabd216434749 + depends: + - __glibc >=2.28 + - kernel-headers_linux-64 4.18.0 he073ed8_9 + - tzdata + license: LGPL-2.0-or-later AND LGPL-2.0-or-later WITH exceptions AND GPL-2.0-or-later + license_family: GPL + run_exports: + strong: + - __glibc >=2.28,<3.0.a0 + size: 24008591 + timestamp: 1765578833462 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + sha256: b928c30ddcb0e3f544c6eade8352737e6e610e263276b90232db6a578ef899d8 + md5: fcb489df604d100968b737f2cb6076c6 + license: LicenseRef-Public-Domain + run_exports: {} + size: 118849 + timestamp: 1784250406640 +- conda: https://conda.anaconda.org/sdebionne/label/esrf-bcu/linux-64/mrdocs-0.8.0-hb0f4dca_0.conda + sha256: e94797f3625b895e0ae0fc8c9edb6d750531416fb216608d60ad990efcf24210 + md5: ba3589b52b90663769840e331ee33b3e + license: Apache-2.0 WITH LLVM-exception + size: 54790420 + timestamp: 1788428774442 diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 0000000000..869546f41d --- /dev/null +++ b/pixi.toml @@ -0,0 +1,30 @@ +# +# Copyright (c) 2026 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) +# +# Official repository: https://github.com/boostorg/gil +# +# Tooling for building the documentation (doc/). See doc/build_antora.sh. +# `mrdocs` comes from a personal channel repackaging a fixed MrDocs dev +# build (0.8.0+47bdd690824e) -- see doc/mrdocs.yml and the upstream issue +# at https://github.com/cppalliance/mrdocs/issues for the segfault it fixes. +# Drop this dependency once that fix is in an official MrDocs release. + +[workspace] +channels = ["sdebionne/label/esrf-bcu", "conda-forge"] +name = "gil" +platforms = ["linux-64"] +version = "0.1.0" + +[feature.docs.dependencies] +nodejs = "20.*" +cxx-compiler = "*" +mrdocs = "*" + +[feature.docs.tasks] +docs-build = "cd doc && bash build_antora.sh" + +[environments] +docs = ["docs"] From c889bcb336a9111f13344bfc846960535a957d77 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 13:05:57 +0200 Subject: [PATCH 23/37] ci: use pixi in the production docs build too docs-prerequisites now sets up the pixi `docs` environment instead of a plain Node install, and generate-doc runs the build through `pixi run -e docs docs-build`. Without this, a real push to develop/master would still hit the antora-cpp-reference-extension's auto-download of the official MrDocs release, which segfaults on GIL's headers -- validated end-to-end on the docs-ci-smoke test branch first. Co-Authored-By: Claude Sonnet 5 --- .github/actions/docs-prerequisites/action.yml | 4 ++-- .github/actions/generate-doc/action.yml | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/actions/docs-prerequisites/action.yml b/.github/actions/docs-prerequisites/action.yml index c52a8fbfc2..a75f83723d 100644 --- a/.github/actions/docs-prerequisites/action.yml +++ b/.github/actions/docs-prerequisites/action.yml @@ -3,6 +3,6 @@ description: 'Downloads all the necessary packages for building documentation' runs: using: composite steps: - - uses: actions/setup-node@v4 + - uses: prefix-dev/setup-pixi@v0.8.1 with: - node-version: 20 + environments: docs diff --git a/.github/actions/generate-doc/action.yml b/.github/actions/generate-doc/action.yml index dff8544db2..0badbdb11b 100644 --- a/.github/actions/generate-doc/action.yml +++ b/.github/actions/generate-doc/action.yml @@ -11,9 +11,7 @@ runs: # BOOST_SRC_DIR (cross-library headers for MrDocs) comes from # ../boost-root. export BOOST_SRC_DIR="$(cd ../boost-root && pwd)" - cd doc - bash build_antora.sh - cd .. + pixi run -e docs docs-build chmod +x $GITHUB_WORKSPACE/.github/actions/generate-doc/docs-config.sh $GITHUB_WORKSPACE/.github/actions/generate-doc/docs-config.sh shell: bash From 5cda6cfc37bc9bbb237d4b5fb357c265611255f5 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 15:33:41 +0200 Subject: [PATCH 24/37] ci: fix Deploy step's publish_dir after the git-repo-copy fix generate-doc's earlier fix (running the build from the original checkout instead of the .git-less copy under ../boost-root/libs/gil) also moved where docs-config.sh creates temp-doc: it's now at $GITHUB_WORKSPACE/temp-doc, not ../boost-root/libs/gil/temp-doc. docs.yaml's Deploy step still pointed at the old path, so it failed with: ENOENT: no such file or directory, scandir '.../boost-root/libs/gil/temp-doc' (and a confusing secondary "branch gh-pages already exists" error from peaceiris/actions-gh-pages mishandling that missing directory). Co-Authored-By: Claude Sonnet 5 --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index e19faae4f0..a75ab73e3a 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -19,6 +19,6 @@ jobs: uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ../boost-root/libs/gil/temp-doc + publish_dir: temp-doc user_name: 'github-actions[bot]' user_email: 'github-actions[bot]@users.noreply.github.com' From 930bf8e807beb43473804e3e754c7232de30b45a Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 22:22:42 +0200 Subject: [PATCH 25/37] docs: add documentation for sharpening and distance_transform --- doc/modules/ROOT/nav.adoc | 2 + doc/modules/ROOT/pages/examples.adoc | 4 + .../image_processing/distance_transform.adoc | 110 ++++++++++++++++++ .../pages/image_processing/sharpening.adoc | 84 +++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 doc/modules/ROOT/pages/image_processing/distance_transform.adoc create mode 100644 doc/modules/ROOT/pages/image_processing/sharpening.adoc diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 2ed2ee2ed8..2fff253162 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -29,6 +29,8 @@ ** xref:image_processing/overview.adoc[] ** xref:image_processing/basics.adoc[] ** xref:image_processing/affine-region-detectors.adoc[] +** xref:image_processing/sharpening.adoc[] +** xref:image_processing/distance_transform.adoc[] ** Contrast Enhancement *** xref:image_processing/contrast_enhancement/index.adoc[] *** xref:image_processing/contrast_enhancement/overview.adoc[] diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index 9a4faeb46b..0c463ee1e7 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -24,3 +24,7 @@ The following example programs, shipped with GIL, demonstrate various features o Numeric extension) * link:https://github.com/boostorg/gil/blob/develop/example/convolution.cpp[convolution.cpp]: Blurring images (requires the optional Numeric extension) +* link:https://github.com/boostorg/gil/blob/develop/example/sharpening.cpp[sharpening.cpp]: + Sharpening an image using unsharp masking +* link:https://github.com/boostorg/gil/blob/develop/example/euclidean_distance_transform.cpp[euclidean_distance_transform.cpp]: + Computing the distance transform of a binary image diff --git a/doc/modules/ROOT/pages/image_processing/distance_transform.adoc b/doc/modules/ROOT/pages/image_processing/distance_transform.adoc new file mode 100644 index 0000000000..12d52c2d92 --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/distance_transform.adoc @@ -0,0 +1,110 @@ += Distance Transform + +== Description + +The https://en.wikipedia.org/wiki/Distance_transform[distance transform] +computes, for every pixel of a binary (grayscale, two-level) image, the +distance to the nearest "on" or "off" pixel. The result is a grayscale image +where each pixel's value represents that distance. + +`distance_transform` operates on grayscale image views only. A pixel is +considered "off" when its value equals the minimum value of the channel type +(e.g. `0` for `uint8_t`), and "on" otherwise. + +== Parameters + +[cols="1,3"] +|=== +|Parameter |Description + +|`src_view` +|Source (binary) grayscale image view. + +|`dst_view` +|Destination grayscale image view, of the same dimensions as `src_view`, +receiving the computed distances. + +|`dist_from` +|A `distance_from` enum value, either `distance_from::on_pixels` or +`distance_from::off_pixels`, selecting which set of pixels distances are +measured from. + +|`dist_type` +|The distance metric to use, see <>. + +|`mask_size` +|The neighborhood mask size used by the approximate algorithms, see +<>. +|=== + +== Distance types + +[cols="1,3"] +|=== +|Type |Description + +|`distance_type::euclidean_approximation` +|Approximates the Euclidean distance using optimal local distances for a +3x3 or 5x5 neighborhood (Borgefors, 1986). Fast, two-pass algorithm. + +|`distance_type::manhattan` +|City block (`L1`) distance, using a 3x3 or 5x5 neighborhood. + +|`distance_type::chessboard` +|Chessboard (`L`∞) distance, using a 3x3 or 5x5 neighborhood. + +|`distance_type::precise_euclidean` +|Exact Euclidean distance, computed in linear time using the two-pass, +one-dimensional squared distance transform of Felzenszwalb and Huttenlocher. +Requires `mask_size::not_applicable`. +|=== + +== Mask sizes + +`euclidean_approximation`, `manhattan` and `chessboard` require either +`mask_size::three` (3x3 neighborhood) or `mask_size::five` (5x5 +neighborhood); a larger mask trades speed for accuracy. `precise_euclidean` +does not use a neighborhood mask and must be paired with +`mask_size::not_applicable`. Any other combination of `dist_type` and +`mask_size` fails to compile. + +Reference: _Principles of Digital Image Processing: Core Algorithms_, +section 11.2.2, and +http://www.theoryofcomputing.org/articles/v008a019/v008a019.pdf[Felzenszwalb, +Pedro F., and Daniel P. Huttenlocher. "Distance transforms of sampled +functions." Theory of computing 8, no. 1 (2012): 415-428.] + +== Demo + +[source,cpp] +---- +#include +#include +#include + +using namespace boost::gil; + +gray8_image_t input; +read_image("input.png", input, png_tag{}); +gray8_image_t output(input.dimensions()); + +// Approximate Euclidean distance to the nearest 'on' pixel, 3x3 mask. +distance_transform( + view(input), + view(output), + distance_from::on_pixels, + distance_type::euclidean_approximation, + mask_size::three); +write_view("distance.png", view(output), png_tag{}); + +// Exact Euclidean distance. +distance_transform( + view(input), + view(output), + distance_from::on_pixels, + distance_type::precise_euclidean, + mask_size::not_applicable); +---- + +See also the full example at +link:https://github.com/boostorg/gil/blob/develop/example/euclidean_distance_transform.cpp[example/euclidean_distance_transform.cpp]. diff --git a/doc/modules/ROOT/pages/image_processing/sharpening.adoc b/doc/modules/ROOT/pages/image_processing/sharpening.adoc new file mode 100644 index 0000000000..6fef173d38 --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/sharpening.adoc @@ -0,0 +1,84 @@ += Sharpening + +== Description + +`sharpen` sharpens grayscale and RGB images using the +https://en.wikipedia.org/wiki/Unsharp_masking[unsharp masking] (USM) technique. + +The idea is to compute a blurred (low frequency) version of the image, subtract +it from the original to obtain a high frequency "mask" that highlights edges, +and then add that mask back to the original, amplified by a chosen amount. An +optional threshold can be used to avoid sharpening noise that would otherwise +be mistaken for an edge. + +RGB images are converted to the Lab color space, sharpening is applied only to +the `L*` (lightness) channel, leaving `a*` and `b*` untouched, and the result +is converted back to RGB. + +== Algorithm + +For each pixel `I(u, v)`: + +* Compute `I'(u, v)`, a smoothed version of `I` obtained by convolving the + image with a Gaussian kernel of the given `sigma`. +* Compute the local contrast `I(u, v) - I'(u, v)`. +* If `threshold` is greater than zero, discard (zero out) mask values whose + absolute local contrast is below `threshold * max(|local contrast|)`, so + that only sufficiently strong edges get sharpened. +* Apply the (optionally thresholded) mask back onto the original pixel, + scaled by `amount`: ++ +[source] +---- +I(u, v) = I(u, v) + amount * (I(u, v) - I'(u, v)) +---- + +Reference: _Principles of Digital Image Processing - Fundamental Techniques_, +Wilhelm Burger, Mark J. Burge. + +== Parameters + +[cols="1,3"] +|=== +|Parameter |Description + +|`src_view` +|Source image view. Must be grayscale or RGB. + +|`dst_view` +|Destination image view. Must use the same color space as `src_view` and have +the same dimensions. + +|`sigma` +|Standard deviation of the Gaussian kernel used to compute the blurred image. +Must be greater than `0`. Larger values sharpen coarser (lower frequency) +detail. + +|`amount` +|Sharpening weight applied to the high frequency mask. + +|`threshold` +|Minimum local contrast, in the range `[0, 1]`, required for a pixel to be +sharpened. Defaults to `0.0`, which sharpens every pixel. +|=== + +== Demo + +[source,cpp] +---- +#include +#include +#include + +using namespace boost::gil; + +gray8_image_t img_in; +read_image("input.png", img_in, png_tag{}); +gray8_image_t img_out(img_in.dimensions()); + +sharpen(view(img_in), view(img_out), 1, 3); +write_view("sharpened.png", view(img_out), png_tag{}); +---- + +See also the full example at +link:https://github.com/boostorg/gil/blob/develop/example/sharpening.cpp[example/sharpening.cpp]. From 56803ff2a267fa3fa0707467fea9359ee0c72b8b Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 22:36:06 +0200 Subject: [PATCH 26/37] docs: add missing ahe documentation --- doc/modules/ROOT/nav.adoc | 1 + .../adaptive_histogram_equalization.adoc | 93 +++++++++++++++++++ .../contrast_enhancement/overview.adoc | 2 +- 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 doc/modules/ROOT/pages/image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 2fff253162..c8553232d6 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -36,6 +36,7 @@ *** xref:image_processing/contrast_enhancement/overview.adoc[] *** xref:image_processing/contrast_enhancement/histogram_equalization.adoc[] *** xref:image_processing/contrast_enhancement/histogram_matching.adoc[] +*** xref:image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc[] * Histogram ** xref:histogram/index.adoc[] ** xref:histogram/overview.adoc[] diff --git a/doc/modules/ROOT/pages/image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc new file mode 100644 index 0000000000..ed1fa4b02f --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc @@ -0,0 +1,93 @@ +[#ahe] += Adaptive Histogram Equalization + +== Description + +Adaptive Histogram Equalization (AHE) is a variant of +xref:image_processing/contrast_enhancement/histogram_equalization.adoc[histogram equalization] +that computes several histograms, each corresponding to a different section (tile) of the +image, and uses them to redistribute the lightness values of the image locally. + +Unlike plain histogram equalization, which computes a single mapping for the whole image, AHE +adapts to local contrast, which improves detail in both dark and bright regions at once. Applied +without limits, however, it tends to over-amplify noise in flat (near-uniform) tiles. GIL +implements the Contrast Limited variant (CLAHE), which clips each tile's histogram at a +configurable limit and redistributes the clipped, excess count uniformly over the other bins +before computing the mapping. + +`non_overlapping_interpolated_clahe` is named after the specific strategy it implements: the +image is split into non-overlapping tiles, equalized independently, and the tile mappings are +then bilinearly interpolated across the image to remove the tile-boundary artifacts that would +otherwise appear. + +== Algorithm + +. Split the image into non-overlapping tiles of size `tile_width_x` by `tile_width_y`. +. For each tile and channel, compute the histogram of pixel values, using bins of width + `bin_width`. +. Clip the histogram: find the actual clip limit (a bin count) whose value, when every bin above + it is clipped down to it, redistributes exactly `clip_limit * (pixels in tile)` excess pixels; + then clip every bin to that limit and spread the removed excess uniformly across all bins. +. Compute the histogram equalization mapping for each (clipped) tile histogram, as in + xref:image_processing/contrast_enhancement/histogram_equalization.adoc[histogram equalization]. +. For each pixel, bilinearly interpolate between the mappings of the (up to four) tiles whose + centers surround it, so that intensities change smoothly across tile boundaries instead of + jumping at them. + +References: + +http://www.cs.unc.edu/techreports/86-013.pdf[Pizer, Stephen M., et al. "Adaptive Histogram +Equalization and Its Variations." Computer Vision, Graphics, and Image Processing 39, no. 3 +(1987): 355-368.] + +Graphics Gems IV, Paul S. Heckbert (ed.), p. 474 (clip-and-redistribute algorithm). + +== Parameters + +[cols="1,3"] +|=== +|Parameter |Description + +|`src_view` +|Source image view. + +|`dst_view` +|Destination image view, same color space and dimensions as `src_view`. + +|`tile_width_x`, `tile_width_y` +|Tile size, in pixels, along the x and y axes. Defaults to `20` for both. Smaller tiles adapt +more locally but increase the risk of amplifying noise. + +|`clip_limit` +|Fraction of a tile's pixel count above which a histogram bin is clipped, in the range +`(0, 1]`. Defaults to `0.03`. Lower values limit contrast amplification (and noise) more +aggressively. + +|`bin_width` +|Width of each histogram bin. Defaults to `1`. + +|`mask`, `src_mask` +|When `mask` is `true`, `src_mask` selects which pixels of `src_view` are copied unmodified +to `dst_view` instead of being equalized. Defaults to no mask. +|=== + +== Demo + +[source,cpp] +---- +#include +#include +#include + +using namespace boost::gil; + +gray8_image_t img_in; +read_image("test_adaptive.png", img_in, png_tag{}); +gray8_image_t img_out(img_in.dimensions()); + +non_overlapping_interpolated_clahe(view(img_in), view(img_out)); +write_view("out-adaptive.png", view(img_out), png_tag{}); +---- + +See also the full example at +link:https://github.com/boostorg/gil/blob/develop/example/adaptive_histogram_equalization.cpp[example/adaptive_histogram_equalization.cpp]. diff --git a/doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc index dd0c4c9ad7..767702c1e3 100644 --- a/doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc +++ b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc @@ -10,5 +10,5 @@ These include: . xref:image_processing/contrast_enhancement/histogram_equalization.adoc[] . xref:image_processing/contrast_enhancement/histogram_matching.adoc[] -. `ahe` +. xref:image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc[] . Linear and Non-Linear Contrast stretching From 96371cfb0cc33e9720d6002bc284be26f44a018f Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 22:53:19 +0200 Subject: [PATCH 27/37] docs: add documentation for convolution, morphology and thresholding --- doc/modules/ROOT/nav.adoc | 3 + doc/modules/ROOT/pages/examples.adoc | 8 ++ .../pages/image_processing/convolution.adoc | 89 ++++++++++++++++ .../pages/image_processing/morphology.adoc | 91 ++++++++++++++++ .../pages/image_processing/thresholding.adoc | 100 ++++++++++++++++++ 5 files changed, 291 insertions(+) create mode 100644 doc/modules/ROOT/pages/image_processing/convolution.adoc create mode 100644 doc/modules/ROOT/pages/image_processing/morphology.adoc create mode 100644 doc/modules/ROOT/pages/image_processing/thresholding.adoc diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index c8553232d6..02f986ce10 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -31,6 +31,9 @@ ** xref:image_processing/affine-region-detectors.adoc[] ** xref:image_processing/sharpening.adoc[] ** xref:image_processing/distance_transform.adoc[] +** xref:image_processing/morphology.adoc[] +** xref:image_processing/thresholding.adoc[] +** xref:image_processing/convolution.adoc[] ** Contrast Enhancement *** xref:image_processing/contrast_enhancement/index.adoc[] *** xref:image_processing/contrast_enhancement/overview.adoc[] diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index 0c463ee1e7..d26652fa3d 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -28,3 +28,11 @@ The following example programs, shipped with GIL, demonstrate various features o Sharpening an image using unsharp masking * link:https://github.com/boostorg/gil/blob/develop/example/euclidean_distance_transform.cpp[euclidean_distance_transform.cpp]: Computing the distance transform of a binary image +* link:https://github.com/boostorg/gil/blob/develop/example/morphology.cpp[morphology.cpp]: + Applying morphological operations (dilation, erosion, opening, closing, ...) +* link:https://github.com/boostorg/gil/blob/develop/example/threshold.cpp[threshold.cpp]: + Thresholding an image +* link:https://github.com/boostorg/gil/blob/develop/example/adaptive_threshold.cpp[adaptive_threshold.cpp]: + Thresholding an image adaptively, based on local pixel neighborhoods +* link:https://github.com/boostorg/gil/blob/develop/example/convolve2d.cpp[convolve2d.cpp]: + Convolving an image with a 2D kernel diff --git a/doc/modules/ROOT/pages/image_processing/convolution.adoc b/doc/modules/ROOT/pages/image_processing/convolution.adoc new file mode 100644 index 0000000000..72725d9dd5 --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/convolution.adoc @@ -0,0 +1,89 @@ += Convolution + +== Description + +`convolve.hpp` provides +xref:image_processing/basics.adoc#_convolution[convolution] of an image with +a kernel, in two flavors: separable one-dimensional convolution along rows +and/or columns, and general two-dimensional convolution. Kernels come from +`kernel.hpp` and can be fixed-size (`kernel_1d_fixed`, `kernel_2d_fixed`, +known at compile time) or resizable (`kernel_1d`, `kernel_2d`). + +== Separable (1D) convolution + +Many kernels, such as the Gaussian kernel, are _separable_: convolving with +the full 2D kernel gives the same result as convolving with a 1D kernel along +the rows, then convolving the result with a 1D kernel along the columns. This +is significantly cheaper than a full 2D convolution. + +`convolve_rows` and `convolve_cols` (and their `_fixed` counterparts, for +fixed-size kernels) convolve along one axis: + +[source,cpp] +---- +#include +#include + +// radius-1 Gaussian kernel, size 9 +float gaussian[] = { + 0.00022923296f, 0.0059770769f, 0.060597949f, 0.24173197f, 0.38292751f, + 0.24173197f, 0.060597949f, 0.0059770769f, 0.00022923296f}; +kernel_1d_fixed kernel(gaussian, 4); + +rgb8_image_t convolved(img); +convolve_rows_fixed(const_view(img), kernel, view(convolved)); +convolve_cols_fixed(const_view(convolved), kernel, view(convolved)); +---- + +`PixelAccum`, the explicit template argument (`rgb32f_pixel_t` above), is the +pixel type used to accumulate intermediate sums; it should have a channel +type wide and precise enough to avoid overflow or precision loss, typically a +floating point pixel type matching the source's color space. + +== 2D convolution + +`convolve_2d` (in the `detail` namespace) convolves the image directly with a +2D kernel, useful when the kernel is not separable: + +[source,cpp] +---- +std::vector v(9, 1.0f / 9.0f); // 3x3 mean/box filter +detail::kernel_2d kernel(v.begin(), v.size(), 1, 1); +detail::convolve_2d(view(img), kernel, view(img_out)); +---- + +== Boundary handling + +Pixels near the border don't have a full neighborhood available. `convolve_rows`, +`convolve_cols` and their `_fixed` counterparts accept a `boundary_option` +(defined in `algorithm.hpp`) to control how out-of-bounds pixels are handled: + +[cols="1,3"] +|=== +|Option |Description + +|`output_ignore` +|Leave the corresponding output pixels unchanged. + +|`output_zero` +|Set the corresponding output pixels to zero. + +|`extend_padded` +|Assume the source view is already padded with extra boundary pixels. + +|`extend_zero` +|Treat out-of-bounds source pixels as zero. + +|`extend_constant` +|Treat out-of-bounds source pixels as equal to the nearest edge pixel. +|=== + +`convolve_2d` only supports `extend_zero` boundary handling. + +== Demo + +See the full examples at +link:https://github.com/boostorg/gil/blob/develop/example/convolution.cpp[example/convolution.cpp] +(separable convolution) and +link:https://github.com/boostorg/gil/blob/develop/example/convolve2d.cpp[example/convolve2d.cpp] +(2D convolution). diff --git a/doc/modules/ROOT/pages/image_processing/morphology.adoc b/doc/modules/ROOT/pages/image_processing/morphology.adoc new file mode 100644 index 0000000000..2097dfd711 --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/morphology.adoc @@ -0,0 +1,91 @@ += Morphology + +== Description + +https://en.wikipedia.org/wiki/Mathematical_morphology[Morphological +operations] process an image based on the shape of a +_structuring element_ — a small binary matrix, represented in GIL by a +`kernel_2d`, that is compared against the neighborhood of each pixel. They +are most commonly applied to binary (black and white) images, for example to +remove noise or extract shape features, but GIL's implementation works on +grayscale channel values too. + +`morphology.hpp` defines two base operations, `dilate` and `erode`, and a +number of operations built out of them by chaining them or subtracting their +results. + +TIP: Morphological operations expect a binary image. Use +xref:image_processing/thresholding.adoc#_binary_threshold[`threshold_binary`] +to convert a grayscale image first. + +== Base operations + +`dilate` replaces each pixel with the *maximum* value found within the +structuring element's neighborhood; it grows bright regions and shrinks dark +ones. `erode` replaces each pixel with the *minimum* value found in the +neighborhood, having the opposite effect. Both accept an `iterations` +parameter to apply the operation repeatedly. + +[source,cpp] +---- +// dilate(src_view, dst_view, structuring_element, iterations) +dilate(view(img), view(img_out), ker_mat, 1); +erode(view(img), view(img_out), ker_mat, 1); +---- + +== Compound operations + +[cols="1,3"] +|=== +|Operation |Description + +|`opening` +|Erosion followed by dilation. Removes small bright spots (noise) while +leaving the overall shape and size of larger bright regions largely intact. + +|`closing` +|Dilation followed by erosion, the opposite of opening. Closes small dark +holes inside bright regions. + +|`morphological_gradient` +|Difference between the dilation and the erosion of the image. Highlights +the outline of objects in the image. + +|`top_hat` +|Difference between the image and its opening. Extracts small bright +elements and details that opening would otherwise remove. + +|`black_hat` +|Difference between the closing of the image and the image itself. Extracts +small dark elements and details that closing would otherwise remove. +|=== + +[source,cpp] +---- +// opening(src_view, dst_view, structuring_element) +opening(view(img), view(img_out), ker_mat); +closing(view(img), view(img_out), ker_mat); + +// morphological_gradient/top_hat/black_hat(src_view, dst_view, structuring_element) +morphological_gradient(view(img), view(img_out), ker_mat); +top_hat(view(img), view(img_out), ker_mat); +black_hat(view(img), view(img_out), ker_mat); +---- + +== Structuring element + +The structuring element is a `kernel_2d` of `0`s and `1`s: only the pixels +overlapped by a `1` participate in the min/max comparison. A common choice is +a square of `1`s, which makes every pixel in the neighborhood participate: + +[source,cpp] +---- +std::vector ker_vec(9, 1.0f); // 3x3 structuring element, all ones +gil::detail::kernel_2d ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1); +---- + +== Demo + +See the full example at +link:https://github.com/boostorg/gil/blob/develop/example/morphology.cpp[example/morphology.cpp], +which applies each operation from the command line to a PNG image. diff --git a/doc/modules/ROOT/pages/image_processing/thresholding.adoc b/doc/modules/ROOT/pages/image_processing/thresholding.adoc new file mode 100644 index 0000000000..83347ddb37 --- /dev/null +++ b/doc/modules/ROOT/pages/image_processing/thresholding.adoc @@ -0,0 +1,100 @@ += Thresholding + +== Description + +Thresholding segments an image by comparing each pixel's channel value against +a threshold, producing pixels classified as either "foreground" or +"background", or an image with saturated (clipped) intensities. + +All functions described here operate on a per-channel basis, so they apply +equally to grayscale and multi-channel (e.g. RGB) images, and accept a +`threshold_direction`: + +* `threshold_direction::regular` treats values greater than the threshold as + foreground. +* `threshold_direction::inverse` treats values less than or equal to the + threshold as foreground. + +== Binary threshold + +`threshold_binary` sets each pixel to `max_value` or `0`, depending on +whether it lies on the foreground or background side of `threshold_value` +(swapped when `direction` is `inverse`). If `max_value` is omitted, it +defaults to the maximum value representable by the destination channel type. + +[source,cpp] +---- +#include + +// values above 150 become 255, others become 0 +threshold_binary(view(img), view(img_out), 150, 255); +---- + +== Truncating threshold + +`threshold_truncate` leaves foreground pixels either clipped to +`threshold_value` or set to `0`, depending on `threshold_truncate_mode`: + +* `threshold_truncate_mode::threshold` clips foreground pixels to + `threshold_value` and leaves background pixels unchanged. +* `threshold_truncate_mode::zero` zeroes background pixels and leaves + foreground pixels unchanged. + +[source,cpp] +---- +// values above 150 are clipped down to 150, others are left unchanged +threshold_truncate(view(img), view(img_out), 150, threshold_truncate_mode::threshold); +---- + +== Optimal threshold + +`threshold_optimal` computes the threshold value itself instead of taking one +as a parameter. The only method currently implemented, +`threshold_optimal_value::otsu`, picks the threshold that +https://en.wikipedia.org/wiki/Otsu%27s_method[minimizes intra-class intensity +variance] between the two resulting classes, then applies it as a binary +threshold. + +[source,cpp] +---- +threshold_optimal(view(img), view(img_out), threshold_optimal_value::otsu); +---- + +== Adaptive threshold + +Unlike the fixed thresholds above, `threshold_adaptive` computes a different +threshold for each pixel, based on the pixels in its local neighborhood. This +copes better than a single, fixed threshold with images where illumination +varies across the frame. + +For each pixel, the local threshold is the value of a blurred version of the +image at that pixel (optionally offset by `constant`), where the blur is +computed by convolving with a `kernel_size` x `kernel_size` kernel chosen by +`threshold_adaptive_method`: + +* `threshold_adaptive_method::mean` averages the neighborhood uniformly. +* `threshold_adaptive_method::gaussian` weights the neighborhood with a + Gaussian kernel, giving nearby pixels more influence than distant ones. + +`kernel_size` must be odd. If `max_value` is omitted, it defaults to the +maximum value representable by the destination channel type. + +[source,cpp] +---- +#include + +// mean-adaptive threshold with an 11x11 neighborhood, offset by 2 +threshold_adaptive(view(img), view(img_out), 11, threshold_adaptive_method::mean, + threshold_direction::regular, 2); + +// gaussian-adaptive threshold with a 7x7 neighborhood, offset by 2 +threshold_adaptive(view(img), view(img_out), 7, threshold_adaptive_method::gaussian, + threshold_direction::regular, 2); +---- + +== Demo + +See the full examples at +link:https://github.com/boostorg/gil/blob/develop/example/threshold.cpp[example/threshold.cpp] +and +link:https://github.com/boostorg/gil/blob/develop/example/adaptive_threshold.cpp[example/adaptive_threshold.cpp]. From 007047b7328be728a64003076a37dc243b14da9b Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 22:58:02 +0200 Subject: [PATCH 28/37] docs: in contrast_enhancement merge overview into index --- doc/modules/ROOT/nav.adoc | 1 - .../contrast_enhancement/index.adoc | 14 ++++++++++++-- .../contrast_enhancement/overview.adoc | 14 -------------- 3 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 02f986ce10..423f048ce9 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -36,7 +36,6 @@ ** xref:image_processing/convolution.adoc[] ** Contrast Enhancement *** xref:image_processing/contrast_enhancement/index.adoc[] -*** xref:image_processing/contrast_enhancement/overview.adoc[] *** xref:image_processing/contrast_enhancement/histogram_equalization.adoc[] *** xref:image_processing/contrast_enhancement/histogram_matching.adoc[] *** xref:image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc[] diff --git a/doc/modules/ROOT/pages/image_processing/contrast_enhancement/index.adoc b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/index.adoc index 536f4f0620..241c67ac38 100644 --- a/doc/modules/ROOT/pages/image_processing/contrast_enhancement/index.adoc +++ b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/index.adoc @@ -1,4 +1,14 @@ = Contrast Enhancement -The GIL documentation sections listed below are dedicated to describe image -processing algorithms used for contrast enhancement. +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 are also +prevalent. + +We have a few contrast enhancement algorithms in the GIL image processing suite as well. +These include: + +. xref:image_processing/contrast_enhancement/histogram_equalization.adoc[] +. xref:image_processing/contrast_enhancement/histogram_matching.adoc[] +. xref:image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc[] +. Linear and Non-Linear Contrast stretching diff --git a/doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc b/doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc deleted file mode 100644 index 767702c1e3..0000000000 --- a/doc/modules/ROOT/pages/image_processing/contrast_enhancement/overview.adoc +++ /dev/null @@ -1,14 +0,0 @@ -= 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 are also -prevalent. - -We have a few contrast enhancement algorithms in the GIL image processing suite as well. -These include: - -. xref:image_processing/contrast_enhancement/histogram_equalization.adoc[] -. xref:image_processing/contrast_enhancement/histogram_matching.adoc[] -. xref:image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc[] -. Linear and Non-Linear Contrast stretching From 3cf4ace229cfff68feed6f65f3bbd923e62bb7b7 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 3 Sep 2026 23:07:03 +0200 Subject: [PATCH 29/37] docs: move histogram section under image processing --- doc/modules/ROOT/nav.adoc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 423f048ce9..eb633530c3 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -39,21 +39,21 @@ *** xref:image_processing/contrast_enhancement/histogram_equalization.adoc[] *** xref:image_processing/contrast_enhancement/histogram_matching.adoc[] *** xref:image_processing/contrast_enhancement/adaptive_histogram_equalization.adoc[] -* Histogram -** xref:histogram/index.adoc[] -** xref:histogram/overview.adoc[] -** xref:histogram/create.adoc[] -** xref:histogram/fill.adoc[] -** xref:histogram/subhistogram.adoc[] -** xref:histogram/cumulative.adoc[] -** xref:histogram/stl_compatibility.adoc[] -** xref:histogram/utilities.adoc[] -** xref:histogram/extend.adoc[] -** xref:histogram/limitations.adoc[] -** Extension -*** xref:histogram/extension/index.adoc[] -*** xref:histogram/extension/overview.adoc[] -*** xref:histogram/extension/std.adoc[] +** Histogram +*** xref:histogram/index.adoc[] +*** xref:histogram/overview.adoc[] +*** xref:histogram/create.adoc[] +*** xref:histogram/fill.adoc[] +*** xref:histogram/subhistogram.adoc[] +*** xref:histogram/cumulative.adoc[] +*** xref:histogram/stl_compatibility.adoc[] +*** xref:histogram/utilities.adoc[] +*** xref:histogram/extend.adoc[] +*** xref:histogram/limitations.adoc[] +*** Extension +**** xref:histogram/extension/index.adoc[] +**** xref:histogram/extension/overview.adoc[] +**** xref:histogram/extension/std.adoc[] * Extensions ** xref:io.adoc[] ** xref:toolbox.adoc[] From 672dd5f8d7437d899fdfde20c31ee8d5c2c7a707 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Mon, 7 Sep 2026 13:04:49 +0200 Subject: [PATCH 30/37] ci: drop the custom MrDocs pin now that the segfault is fixed upstream MrDocs 2026.9.4 (official, non-prerelease release) fixes the segfault on GIL's CRTP-based headers that motivated pinning a personal build. The other bug that motivated it -- reference pages linking to private-member sub-pages MrDocs never emits -- is still open upstream (github.com/cppalliance/mrdocs/issues/1277). Oddly, GIL's specific repro from that issue passes cleanly against the tagged 2026.9.4 release binary itself, but fails again against the current `develop` tip (one commit newer) -- so this is unresolved/regressed, not fixed, regardless of that one data point. - docs-prerequisites / generate-doc: back to plain actions/setup-node@v4 + `bash build_antora.sh`, matching boostorg/url, redis and graph -- no cppalliance GitHub Action exists for MrDocs itself (it's resolved by the @cppalliance/antora-cpp-reference-extension npm package, not an Action), so this is the standard pattern other Boost libraries already use. - pixi.toml: drop the sdebionne/label/esrf-bcu channel and the mrdocs dependency; keeps Node + a C++ compiler for local `pixi run -e docs docs-build`, per request pixi itself stays for local dev. - build_antora.sh: drop the MRDOCS_ROOT/CONDA_PREFIX detection block. One deliberate remaining guard: local-playbook.yml keeps `failure_level: fatal` rather than `error`. Beyond #1277 itself, the extension's release selection always considers cppalliance/mrdocs' rolling `develop`/`master` tags (verified in its source: `allowMaster`/`allowDevelop` are hardcoded to `true` regardless of config, so a `version:` pin can't exclude them), so the exact MrDocs build used here isn't actually pinned and can vary from run to run. `fatal` keeps that class of cosmetic dangling-xref noise from hard-failing the build while still catching a real extraction failure. Co-Authored-By: Claude Sonnet 5 --- .github/actions/docs-prerequisites/action.yml | 4 ++-- .github/actions/generate-doc/action.yml | 4 +++- doc/build_antora.sh | 13 ------------- doc/local-playbook.yml | 17 ++++++++++------- doc/mrdocs.yml | 8 ++++---- pixi.lock | 9 --------- pixi.toml | 13 ++++++------- 7 files changed, 25 insertions(+), 43 deletions(-) diff --git a/.github/actions/docs-prerequisites/action.yml b/.github/actions/docs-prerequisites/action.yml index a75f83723d..c52a8fbfc2 100644 --- a/.github/actions/docs-prerequisites/action.yml +++ b/.github/actions/docs-prerequisites/action.yml @@ -3,6 +3,6 @@ description: 'Downloads all the necessary packages for building documentation' runs: using: composite steps: - - uses: prefix-dev/setup-pixi@v0.8.1 + - uses: actions/setup-node@v4 with: - environments: docs + node-version: 20 diff --git a/.github/actions/generate-doc/action.yml b/.github/actions/generate-doc/action.yml index 0badbdb11b..dff8544db2 100644 --- a/.github/actions/generate-doc/action.yml +++ b/.github/actions/generate-doc/action.yml @@ -11,7 +11,9 @@ runs: # BOOST_SRC_DIR (cross-library headers for MrDocs) comes from # ../boost-root. export BOOST_SRC_DIR="$(cd ../boost-root && pwd)" - pixi run -e docs docs-build + cd doc + bash build_antora.sh + cd .. 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/doc/build_antora.sh b/doc/build_antora.sh index 6d9c8c4568..8fd11624d4 100644 --- a/doc/build_antora.sh +++ b/doc/build_antora.sh @@ -26,19 +26,6 @@ fi export BOOST_SRC_DIR="${BOOST_SRC_DIR:-$(realpath "$SCRIPT_DIR/../../..")}" echo "Using BOOST_SRC_DIR=$BOOST_SRC_DIR" -# Pin the @cppalliance/antora-cpp-reference-extension to a local MrDocs -# instead of it downloading the latest GitHub release, which as of this -# writing still segfaults on GIL's headers (upstream fix not released yet). -# When running under `pixi run -e docs`, CONDA_PREFIX/bin/mrdocs is the -# package from the sdebionne/label/esrf-bcu channel (see pixi.toml), which -# repackages the fixed dev build. Falls through to auto-download otherwise. -if [ -z "${MRDOCS_ROOT:-}" ] && [ -n "${CONDA_PREFIX:-}" ] && [ -x "$CONDA_PREFIX/bin/mrdocs" ]; then - export MRDOCS_ROOT="$CONDA_PREFIX" -fi -if [ -n "${MRDOCS_ROOT:-}" ]; then - echo "Using MRDOCS_ROOT=$MRDOCS_ROOT" -fi - echo "Generating the MrDocs compilation database..." # See mrdocs.yml / mrdocs-src/all.cpp: a single translation unit is enough # for MrDocs to extract the whole public API, and is far faster than trying diff --git a/doc/local-playbook.yml b/doc/local-playbook.yml index 768ac3499e..d5e92e275d 100644 --- a/doc/local-playbook.yml +++ b/doc/local-playbook.yml @@ -47,11 +47,14 @@ output: runtime: log: - # `fatal`, not `error`: some MrDocs-generated reference pages link to - # private-member sub-pages (e.g. packed_pixel's `_bitfield`) that MrDocs - # doesn't actually emit, regardless of extract-private -- a small, - # cosmetic upstream issue unrelated to this migration, logged as `error` - # by Antora. `failure_level` is a floor, not a ceiling: `error` messages - # still fail the build at `warn`. Once the MrDocs issue is fixed this - # should go back to `error`. + # `fatal`, not `error`: MrDocs still has an open bug where some generated + # reference pages link to private-member sub-pages it never actually + # emits (github.com/cppalliance/mrdocs/issues/1277). On top of that, + # @cppalliance/antora-cpp-reference-extension's release-selection logic + # always considers cppalliance/mrdocs' rolling `develop`/`master` release + # tags first regardless of any `version:` pin here, so the exact MrDocs + # build used isn't pinned and can vary run to run. Don't let the + # resulting dangling-xref noise hard-fail the build; a real extraction + # failure still raises above `fatal`. Safe to tighten back to `error` + # once #1277 is closed. failure_level: fatal diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index fe4f419785..327e6a414c 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -6,7 +6,7 @@ # # Official repository: https://github.com/boostorg/gil # -# Wired into antora.yml as ext: cpp-reference: config: doc/mrdocs.yml. +# Wired into antora.yml as ext: cppReference: config: doc/mrdocs.yml. # # GIL's extension/io/{jpeg,png,tiff,bmp,pnm,raw} backends need the matching # external image libraries just to parse, so mrdocs-src/all.cpp deliberately @@ -17,9 +17,9 @@ # # `compilation-database` is generated by build_antora.sh (gitignored) since # it needs the resolved $BOOST_SRC_DIR baked into its include path. The file -# must be literally named `compile_commands.json` -- MrDocs 0.8.0+47bdd690824e -# silently rejects any other basename with "Invalid compilation database -# path", even given as an absolute path to a file that exists and parses. +# must be literally named `compile_commands.json` -- MrDocs silently rejects +# any other basename with "Invalid compilation database path", even given +# as an absolute path to a file that exists and parses. source-root: .. input: diff --git a/pixi.lock b/pixi.lock index ba1c3e5aea..6a6a9f3e86 100644 --- a/pixi.lock +++ b/pixi.lock @@ -9,12 +9,10 @@ platforms: environments: default: channels: - - url: https://conda.anaconda.org/sdebionne/label/esrf-bcu/ - url: https://conda.anaconda.org/conda-forge/ packages: {} docs: channels: - - url: https://conda.anaconda.org/sdebionne/label/esrf-bcu/ - url: https://conda.anaconda.org/conda-forge/ packages: linux-64: @@ -44,7 +42,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.3.0-hb2c5482_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - - conda: https://conda.anaconda.org/sdebionne/label/esrf-bcu/linux-64/mrdocs-0.8.0-hb0f4dca_0.conda packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 @@ -373,9 +370,3 @@ packages: run_exports: {} size: 118849 timestamp: 1784250406640 -- conda: https://conda.anaconda.org/sdebionne/label/esrf-bcu/linux-64/mrdocs-0.8.0-hb0f4dca_0.conda - sha256: e94797f3625b895e0ae0fc8c9edb6d750531416fb216608d60ad990efcf24210 - md5: ba3589b52b90663769840e331ee33b3e - license: Apache-2.0 WITH LLVM-exception - size: 54790420 - timestamp: 1788428774442 diff --git a/pixi.toml b/pixi.toml index 869546f41d..2ccd6cdacc 100644 --- a/pixi.toml +++ b/pixi.toml @@ -6,14 +6,14 @@ # # Official repository: https://github.com/boostorg/gil # -# Tooling for building the documentation (doc/). See doc/build_antora.sh. -# `mrdocs` comes from a personal channel repackaging a fixed MrDocs dev -# build (0.8.0+47bdd690824e) -- see doc/mrdocs.yml and the upstream issue -# at https://github.com/cppalliance/mrdocs/issues for the segfault it fixes. -# Drop this dependency once that fix is in an official MrDocs release. +# Tooling for building the documentation (doc/) locally. See +# doc/build_antora.sh -- MrDocs itself is fetched automatically by +# @cppalliance/antora-cpp-reference-extension (the latest release, as of +# MrDocs 2026.9.4 that's a fixed build for GIL's headers), so it isn't a +# pixi dependency. [workspace] -channels = ["sdebionne/label/esrf-bcu", "conda-forge"] +channels = ["conda-forge"] name = "gil" platforms = ["linux-64"] version = "0.1.0" @@ -21,7 +21,6 @@ version = "0.1.0" [feature.docs.dependencies] nodejs = "20.*" cxx-compiler = "*" -mrdocs = "*" [feature.docs.tasks] docs-build = "cd doc && bash build_antora.sh" From 1b86699ced4244323f3251622ca285a23ff8bf58 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Mon, 7 Sep 2026 13:21:21 +0200 Subject: [PATCH 31/37] docs: fix README's stale Doxygen references Leftover from the interim-Doxygen phase of the MrDocs migration and never updated when MrDocs replaced it -- doxyfile doesn't even exist anymore. Co-Authored-By: Claude Sonnet 5 --- .gitignore | 3 ++- doc/README.md | 15 ++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index bbc4675887..6e91d0a50b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,8 @@ stdcerr /doc/build/ /io/output -# Documentation (Antora + Doxygen) +# Documentation (Antora + MrDocs) +.tools doc/html doc/warnings.txt doc/node_modules diff --git a/doc/README.md b/doc/README.md index dc5614c2b5..f078ff425b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -3,17 +3,18 @@ A simple guide about writing and building documentation for Boost.GIL. The documentation is written in AsciiDoc and built with [Antora](https://antora.org/). -The C++ API reference is still generated with [Doxygen](https://www.doxygen.org) -(see `doxyfile`) and stitched into the Antora site by -`modules/ROOT/pages/reference.adoc` — this library is expected to move to a -[MrDocs](https://github.com/cppalliance/mrdocs)-generated, AsciiDoc-native -reference once an upstream MrDocs crash on GIL's header patterns is fixed; -see `mrdocs.yml` for details. +The C++ API reference is generated from the header comments with +[MrDocs](https://github.com/cppalliance/mrdocs) (see `mrdocs.yml` and +`mrdocs-src/all.cpp`) and wired into the Antora site as the `reference:` +module, with `modules/ROOT/pages/reference.adoc` as a curated landing page +into it. MrDocs itself isn't a build dependency here -- it's downloaded +automatically by the `@cppalliance/antora-cpp-reference-extension` Antora +extension the first time the reference is built. ## Prerequisites - [Node.js](https://nodejs.org/) 20+ (for Antora) -- [Doxygen](https://www.doxygen.org) +- A C++ compiler (for MrDocs to parse GIL's headers) Both are available through the repository's `pixi.toml` under the `docs` environment; from the repository root: From c338ff3636b7c6c1818e3d6d6b954dfcb03bb979 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Mon, 7 Sep 2026 21:21:22 +0200 Subject: [PATCH 32/37] Generate doc at doc/html/index.html --- doc/Jamfile | 14 +++++++++----- doc/README.md | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/doc/Jamfile b/doc/Jamfile index e11948448b..08c0a24025 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -1,21 +1,25 @@ -# Boost.GIL (Generic Image Library) - documentation # # Copyright (c) 2018 Stefan Seefeld # Copyright (c) 2026 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) +# # Adapted from Boost.Redis / Boost.Graph, following the boostorg/unordered scheme. -make build/site/index.html : build_antora.sh : @run-script ; +make html/index.html : build_antora.sh : @run-script ; +# Runs the Antora script actions run-script { - bash $(>) + bash -x $(>) } -############################################################################### +# These are used to inform the build system of the +# means to build the integrated and stand-alone docs. + alias boostdoc ; explicit boostdoc ; -alias boostrelease : build/site/index.html ; + +alias boostrelease : html/index.html ; explicit boostrelease ; diff --git a/doc/README.md b/doc/README.md index f078ff425b..843efe9c93 100644 --- a/doc/README.md +++ b/doc/README.md @@ -30,4 +30,4 @@ $ cd libs/gil/doc $ bash build_antora.sh ``` -The site is generated at `doc/build/site/index.html`. +The site is generated at `doc/html/index.html`. From 6777e3bb65282be990affa07b2ae6581b215985d Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Tue, 8 Sep 2026 21:33:42 +0200 Subject: [PATCH 33/37] Fix doc build not properly copied to website --- doc/CMakeLists.txt | 26 ++ doc/Jamfile | 2 +- doc/antora.yml | 4 +- doc/build_antora.sh | 44 +--- doc/{local-playbook.yml => gil-playbook.yml} | 35 ++- doc/{mrdocs-src/all.cpp => mrdocs.cpp} | 11 +- doc/mrdocs.yml | 38 +-- doc/package-lock.json | 81 +------ doc/package.json | 11 +- package-lock.json | 6 + pixi.lock | 241 +++++++++++++++++++ pixi.toml | 1 + 12 files changed, 323 insertions(+), 177 deletions(-) create mode 100644 doc/CMakeLists.txt rename doc/{local-playbook.yml => gil-playbook.yml} (84%) rename doc/{mrdocs-src/all.cpp => mrdocs.cpp} (81%) create mode 100644 package-lock.json diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000000..21d11af281 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# Copyright (c) 2026 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) +# + +# Root CMakeLists.txt so MrDocs knows how to build our code +cmake_minimum_required(VERSION 3.8...3.22) + +# Project +project(boost_gil_mrdocs LANGUAGES CXX) + +# MrDocs forces CMAKE_EXPORT_COMPILE_COMMANDS=ON, incorrectly +# causing all targets to be dumped to the compilation database. +# Disable this setting so we can set EXPORT_COMPILE_COMMANDS +# only to our target of interest +set(CMAKE_EXPORT_COMPILE_COMMANDS OFF) + +# Add Boost +add_subdirectory($ENV{BOOST_SRC_DIR} deps/boost) + +# Add the target for mrdocs to analyze +add_executable(mrdocs mrdocs.cpp) +target_link_libraries(mrdocs PRIVATE Boost::gil) +set_target_properties(mrdocs PROPERTIES EXPORT_COMPILE_COMMANDS ON) diff --git a/doc/Jamfile b/doc/Jamfile index 08c0a24025..f1e37a418a 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -6,7 +6,7 @@ # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # -# Adapted from Boost.Redis / Boost.Graph, following the boostorg/unordered scheme. +# Adapted from Boost.Redis make html/index.html : build_antora.sh : @run-script ; # Runs the Antora script diff --git a/doc/antora.yml b/doc/antora.yml index bc7cf3b6b8..8deb1266b6 100644 --- a/doc/antora.yml +++ b/doc/antora.yml @@ -4,12 +4,10 @@ # 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) # -# Official repository: https://github.com/boostorg/gil -# name: gil -version: ~ title: Boost.GIL +version: ~ start_page: index.adoc nav: - modules/ROOT/nav.adoc diff --git a/doc/build_antora.sh b/doc/build_antora.sh index 8fd11624d4..593d3be17b 100644 --- a/doc/build_antora.sh +++ b/doc/build_antora.sh @@ -5,51 +5,15 @@ # 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) # -# Official repository: https://github.com/boostorg/gil -# set -e SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) cd "$SCRIPT_DIR" -if [ -z "${PLAYBOOK:-}" ]; then - PLAYBOOK="local-playbook.yml" -fi - -# Required by the @cppalliance/antora-cpp-reference-extension (see -# local-playbook.yml): a full Boost superproject checkout, so GIL's headers -# resolve cross-library includes (boost/mp11.hpp, boost/stl_interfaces/...). -# `system-env` on that extension's dependency entry means it reuses this -# instead of cloning its own -- CI's setup-boost action already produces one -# at ../../.. from here (boost-root/libs/gil/doc), same as this fallback. -export BOOST_SRC_DIR="${BOOST_SRC_DIR:-$(realpath "$SCRIPT_DIR/../../..")}" -echo "Using BOOST_SRC_DIR=$BOOST_SRC_DIR" - -echo "Generating the MrDocs compilation database..." -# See mrdocs.yml / mrdocs-src/all.cpp: a single translation unit is enough -# for MrDocs to extract the whole public API, and is far faster than trying -# to point it at GIL's real CMakeLists.txt (which needs libjpeg/libpng/ -# libtiff just to configure, for the extension/io backends this deliberately -# excludes -- see mrdocs-src/all.cpp for why). -# The file must be literally named compile_commands.json -- MrDocs rejects -# any other basename even when given as a full, valid, existing path. -cat > compile_commands.json < #include #include @@ -43,3 +39,4 @@ #include #include #include + diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index 327e6a414c..b97e1dcc83 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -4,39 +4,23 @@ # 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) # -# Official repository: https://github.com/boostorg/gil -# -# Wired into antora.yml as ext: cppReference: config: doc/mrdocs.yml. -# -# GIL's extension/io/{jpeg,png,tiff,bmp,pnm,raw} backends need the matching -# external image libraries just to parse, so mrdocs-src/all.cpp deliberately -# doesn't include them; everything else (core + concepts + image_processing + -# the dynamic_image/toolbox/numeric/histogram extensions + the generic io -# layer) is covered. `exclude` below is belt-and-suspenders: those symbols -# are never reachable from mrdocs-src/all.cpp anyway. -# -# `compilation-database` is generated by build_antora.sh (gitignored) since -# it needs the resolved $BOOST_SRC_DIR baked into its include path. The file -# must be literally named `compile_commands.json` -- MrDocs silently rejects -# any other basename with "Invalid compilation database path", even given -# as an absolute path to a file that exists and parses. -source-root: .. -input: - - ../include/boost/gil -recursive: true +source-root: ../include +compilation-database: ./CMakeLists.txt file-patterns: - - '*.hpp' + - "*.hpp" exclude: - ../include/boost/gil/extension/io -compilation-database: compile_commands.json - include-symbols: - - 'boost::gil::**' + - "boost::gil::**" exclude-symbols: - - 'boost::gil::detail' - - 'boost::gil::**::detail' + - "boost::gil::detail" + - "boost::gil::**::detail" generator: adoc multipage: true -base-url: https://github.com/boostorg/gil/blob/develop/ +sort-members: false +base-url: https://github.com/boostorg/gil/blob/master/include/ +use-system-libc: true +warn-if-undocumented: false +warn-no-paramdoc: false diff --git a/doc/package-lock.json b/doc/package-lock.json index bbfd2337e9..bef07e7468 100644 --- a/doc/package-lock.json +++ b/doc/package-lock.json @@ -5,11 +5,8 @@ "packages": { "": { "dependencies": { - "@cppalliance/antora-cpp-reference-extension": "^0.1.1" - }, - "devDependencies": { - "@antora/cli": "^3.1.15", - "@antora/site-generator": "^3.1.15", + "@cppalliance/antora-cpp-reference-extension": "^0.1.1", + "@cppalliance/antora-downloads-extension": "^0.0.2", "antora": "^3.1.15" } }, @@ -17,7 +14,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.2.0.tgz", "integrity": "sha512-IaxhqSl9CvAi5Ieqt3Kc1SlJ11iq4kkWNFAAwUFCv/9L0WbzoczQ+o0y0W2lEONXVCKOBT1yzFBi55149SNwUQ==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/logger": "3.2.0", @@ -32,7 +28,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.2.0.tgz", "integrity": "sha512-bJ5Vl+FMH52xm2jxdcbrEUN3aeoIYj5NGzLGsVwIPewM0RXee0kckbToAFnU0EK0Uw07cBomiJ5hwVELex0/ug==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/logger": "3.2.0", @@ -51,7 +46,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.2.0.tgz", "integrity": "sha512-Q7lTjNFlpQx3SWW8QbNv5P4f1DhiKhYY3PyMifRHc4npuILOU0q4+kmlO1W1swSDDCg5pDjmgwitwAi9VQgvhg==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0", @@ -78,7 +72,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.2.0.tgz", "integrity": "sha512-fUrh08qpgbsjV7noFTWDQaevLC2NAs3gHRJbaTTvULEb+OZRCB0LjEPyvhIAk1wKwkfPqcnjAShJHurH/gXPCg==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/asciidoc-loader": "3.2.0", @@ -94,7 +87,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.2.0.tgz", "integrity": "sha512-+cwQxIIRNI9+BXHk2fff/jvBd17p/AHslAT02ic6tWlgRaORc5y5/dRK+Q9hSjE6vpndE92gyokRn2zsigOUDg==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/asciidoc-loader": "3.2.0" @@ -116,7 +108,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.2.0.tgz", "integrity": "sha512-UqKzDI2JR/Ms88kgUteyqvIikekDBoZZGPo8L79GpvffPBP2CJmhCSSue1IuV0aFtobHuCgWqWtHzsdhy7KJ6Q==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0", @@ -132,7 +123,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.2.0.tgz", "integrity": "sha512-2+bTFjYOuwoWjgtIfpf8bit6OuizI7O8RnXsHzX2y6/PBbm9K8ulvmqtnFk8933P5ILTZ/NSrbgvcFz5BXrY8A==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0", @@ -146,7 +136,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.2.0.tgz", "integrity": "sha512-W7TI0e2doDwpF7YYsYUYJtxFQfDre4egU33eOM8Y7ZEUaAZBweOCiANFgZmfZiCvM3nswLyBtMJqvVzAc8vPTg==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/asciidoc-loader": "3.2.0" @@ -159,7 +148,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.2.0.tgz", "integrity": "sha512-cBr9wYnF6ba4fbbVvs+fR/88BPoqFB9bxdU2z2kWXDsAkQIT/OHLauo+UY+H5+pw/SET1A9bOwMaNlvM4mvfxg==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/logger": "3.2.0", @@ -173,7 +161,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.2.0.tgz", "integrity": "sha512-mAOrE+vDRa0wUcTc3JIZWvEai69vZ1kx6CvZi8ugSYrcWbZxNDp+R5pYWRX9037dZ5e6s5hKKGV503thm9BiSw==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@iarna/toml": "~2.2", @@ -189,7 +176,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.2.0.tgz", "integrity": "sha512-soJbguowD3r7rCMn/r6OMiphXXUwNXUE9MeR6xrDa1RnoTu5mNALIhgI4mBid1CZ/jb+tnJ0WQWVArM1d9EmKw==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/content-classifier": "3.2.0", @@ -203,7 +189,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.2.0.tgz", "integrity": "sha512-bgShOpARuO+1MF50HUsv25C7msyZk6GpaP2EBcjqD3s0Dj51amQL5KMqAbwfKLjTCiAUTGpC/Yb8rGeogUnmYw==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/asciidoc-loader": "3.2.0", @@ -229,7 +214,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.2.0.tgz", "integrity": "sha512-ZnBl03Ha5dLlduVoKGC2kn+n0o8IPwoMzSbu+rLok+uzEPR2La7fQXNJa9exNzBtnE2GnNkj37jJg6YHQpIUiw==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/content-classifier": "3.2.0", @@ -243,7 +227,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.2.0.tgz", "integrity": "sha512-zXYxyH0+NuhaPnGReDvL8CCmYPsocxSKn3HeE3Z39lrexK/qSajliUGYW0jP9IRi+cCFXpvuC7PDYtQfZDvWaw==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/file-publisher": "3.2.0" @@ -256,7 +239,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.2.0.tgz", "integrity": "sha512-EYagiuG2Tk5QtnG/rwTzu/8mxDtL8J2oqZc74g0h4h3iatJeD8yNrDc8iDhNaycEZNaxPRfIK73tpKuIFZw+Iw==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0", @@ -279,7 +261,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@antora/user-require-helper/-/user-require-helper-3.0.0.tgz", "integrity": "sha512-KIXb8WYhnrnwH7Jj21l1w+et9k5GvcgcqvLOwxqWLEd0uVZOiMFdqFjqbVm3M+zcrs1JXWMeh2LLvxBbQs3q/Q==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/expand-path-helper": "~3.0" @@ -292,7 +273,6 @@ "version": "2.2.9", "resolved": "https://registry.npmjs.org/@asciidoctor/core/-/core-2.2.9.tgz", "integrity": "sha512-tIPRHo1T2SFmAm+j77cDsj0RuaszP7xJxsaVTTAF5CwKyTbazw9TnIVlpIWM5yWfIWAWcAZy92RcnPgMJwny1w==", - "dev": true, "license": "MIT", "dependencies": { "asciidoctor-opal-runtime": "0.3.4", @@ -341,11 +321,16 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@cppalliance/antora-downloads-extension": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@cppalliance/antora-downloads-extension/-/antora-downloads-extension-0.0.2.tgz", + "integrity": "sha512-2wXahlvRz9J75ZSfzDeP4XpIZiqIm+w/YjmCWJxFPp6oWgP7e8f6ps7HqdtHNGxnK5mG38OjiCFdHjmHYfgbDA==", + "license": "BSL-1.0" + }, "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "dev": true, "license": "ISC" }, "node_modules/@nodelib/fs.scandir": { @@ -387,7 +372,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", - "dev": true, "license": "MIT" }, "node_modules/abort-controller": { @@ -418,7 +402,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/antora/-/antora-3.2.0.tgz", "integrity": "sha512-wRfXafDYW7FGOgwFf2vpM7cIBai/ms/XsCIrC8Rp+abScyEjlLxvoqjvYRmxRGcaJ3HdZ6mS8WntQlBVkQ9UKw==", - "dev": true, "license": "MPL-2.0", "dependencies": { "@antora/cli": "3.2.0", @@ -441,7 +424,6 @@ "version": "0.3.4", "resolved": "https://registry.npmjs.org/asciidoctor-opal-runtime/-/asciidoctor-opal-runtime-0.3.4.tgz", "integrity": "sha512-zqd6zn1LV+PZ69AP/kEbB00zuPHMIAJY3IX8+aZV+X1qOwatYvKGjsMmdMc5ApfhtkjZ4mYkqiTPJWnEnBiMJg==", - "dev": true, "license": "MIT", "dependencies": { "fast-glob": "~3.3", @@ -467,7 +449,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.0.0" @@ -504,7 +485,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", - "dev": true, "license": "Apache-2.0", "peerDependencies": { "react-native-b4a": "*" @@ -519,7 +499,6 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", - "dev": true, "license": "Apache-2.0", "peerDependencies": { "bare-abort-controller": "*" @@ -590,7 +569,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.0.0" @@ -665,7 +643,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8" @@ -687,7 +664,6 @@ "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "dev": true, "license": "MIT", "engines": { "node": ">=20" @@ -697,7 +673,6 @@ "version": "6.2.5", "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.5.tgz", "integrity": "sha512-JtXpxqDqJ8P0UwEHwhxLzCIXQy97vlYBZR222Sbzb1q1Erex9ASrztJ29SyhWFQjod1AeFBaPzEEC8YvtZMIYg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "lodash.clonedeep": "^4.5.0", @@ -864,7 +839,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "bare-events": "^2.7.0" @@ -874,7 +848,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -1060,7 +1033,6 @@ "version": "4.7.9", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", - "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.5", @@ -1133,7 +1105,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", - "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -1279,7 +1250,6 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", - "dev": true, "funding": [ { "type": "github", @@ -1302,7 +1272,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -1315,7 +1284,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "dev": true, "license": "MIT" }, "node_modules/math-intrinsics": { @@ -1365,7 +1333,6 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -1375,7 +1342,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -1428,7 +1394,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/multi-progress/-/multi-progress-4.0.0.tgz", "integrity": "sha512-9zcjyOou3FFCKPXsmkbC3ethv51SFPoA4dJD6TscIp2pUmy26kBDZW6h9XofPELrzseSkuD7r0V+emGEeo39Pg==", - "dev": true, "license": "MIT", "peerDependencies": { "progress": "^2.0.0" @@ -1438,14 +1403,12 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, "license": "MIT" }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" @@ -1470,14 +1433,12 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, "license": "MIT" }, "node_modules/picomatch": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1499,7 +1460,6 @@ "version": "10.3.1", "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", - "dev": true, "license": "MIT", "dependencies": { "@pinojs/redact": "^0.4.0", @@ -1522,7 +1482,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", - "dev": true, "license": "MIT", "dependencies": { "split2": "^4.0.0" @@ -1532,7 +1491,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", - "dev": true, "license": "MIT" }, "node_modules/possible-typed-array-names": { @@ -1557,7 +1515,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz", "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", - "dev": true, "funding": [ { "type": "github", @@ -1574,7 +1531,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -1613,7 +1569,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "dev": true, "license": "MIT" }, "node_modules/readable-stream": { @@ -1636,7 +1591,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 12.13.0" @@ -1646,14 +1600,12 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true, "license": "ISC" }, "node_modules/replace-ext": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -1716,7 +1668,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1775,7 +1726,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/should-proxy/-/should-proxy-1.0.4.tgz", "integrity": "sha512-RPQhIndEIVUCjkfkQ6rs6sOR6pkxJWCNdxtfG5pP0RVgUYbK5911kLTF0TNcCC0G3YCGd492rMollFT2aTd9iQ==", - "dev": true, "license": "MIT" }, "node_modules/simple-concat": { @@ -1827,7 +1777,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", - "dev": true, "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0" @@ -1837,7 +1786,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -1847,7 +1795,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, "license": "ISC", "engines": { "node": ">= 10.x" @@ -1857,7 +1804,6 @@ "version": "2.28.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", - "dev": true, "license": "MIT", "dependencies": { "events-universal": "^1.0.0", @@ -1878,7 +1824,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", - "dev": true, "license": "MIT", "dependencies": { "streamx": "^2.12.5" @@ -1888,7 +1833,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" @@ -1898,7 +1842,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz", "integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==", - "dev": true, "license": "MIT", "dependencies": { "real-require": "^1.0.0" @@ -1911,7 +1854,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz", "integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==", - "dev": true, "license": "MIT" }, "node_modules/to-buffer": { @@ -1958,7 +1900,6 @@ "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, "license": "BSD-2-Clause", "optional": true, "bin": { @@ -1972,7 +1913,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/unxhr/-/unxhr-1.2.0.tgz", "integrity": "sha512-6cGpm8NFXPD9QbSNx0cD2giy7teZ6xOkCUH3U89WKVkL9N9rBrWjlCwhR94Re18ZlAop4MOc3WU1M3Hv/bgpIw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.11" @@ -1982,7 +1922,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", - "dev": true, "license": "MIT", "dependencies": { "clone": "^2.1.2", @@ -2019,7 +1958,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, "license": "MIT" }, "node_modules/wrappy": { @@ -2041,7 +1979,6 @@ "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -2051,7 +1988,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", - "dev": true, "license": "MIT", "dependencies": { "pend": "~1.2.0" @@ -2064,7 +2000,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/yazl/-/yazl-3.3.1.tgz", "integrity": "sha512-BbETDVWG+VcMUle37k5Fqp//7SDOK2/1+T7X8TD96M3D9G8jK5VLUdQVdVjGi8im7FGkazX7kk5hkU8X4L5Bng==", - "dev": true, "license": "MIT", "dependencies": { "buffer-crc32": "^1.0.0" diff --git a/doc/package.json b/doc/package.json index 9f9e680e77..d442237439 100644 --- a/doc/package.json +++ b/doc/package.json @@ -1,10 +1,7 @@ { - "devDependencies": { - "@antora/cli": "^3.1.15", - "@antora/site-generator": "^3.1.15", - "antora": "^3.1.15" - }, "dependencies": { - "@cppalliance/antora-cpp-reference-extension": "^0.1.1" + "@cppalliance/antora-downloads-extension": "^0.0.2", + "@cppalliance/antora-cpp-reference-extension": "^0.1.1", + "antora": "^3.1.15" } -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..a65747e071 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "gil", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/pixi.lock b/pixi.lock index 6a6a9f3e86..742573aa1d 100644 --- a/pixi.lock +++ b/pixi.lock @@ -18,6 +18,9 @@ environments: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cmake-4.4.3-hff7ac6b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-15.3.0-h8846f6e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-2.0.0-hdab9b82_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-15.3.0-hc6a0c74_4.conda @@ -25,15 +28,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-15.3.0-hc6a0c74_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-15.3.0-h90d9265_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.22.0-ha042cf0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-15.3.0-h54950a5_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.20.2-h4dda4d0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rhash-1.4.6-hb03c661_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda @@ -71,6 +86,53 @@ packages: run_exports: {} size: 3713752 timestamp: 1784214522814 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + sha256: 1a0d382c515ebf55f8ee1f38c8b81bc95af5c2acc42ad53b66bc5df932032f96 + md5: e675fabcf81499adc7edf58124fb1e01 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + run_exports: + weak: + - bzip2 >=1.0.8,<2.0a0 + size: 257808 + timestamp: 1785906269155 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda + sha256: 9c37cc233238adf366ea75b0e845662a5be07ceadf62e458183516369340274b + md5: 3ad2b403be8fc5612b9159e2ce621f69 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + run_exports: + weak: + - c-ares >=1.34.8,<2.0a0 + size: 228700 + timestamp: 1787169971173 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cmake-4.4.3-hff7ac6b_0.conda + sha256: a6d21b5166cd7a44fa975e541f1abf851579a8ac3458e72e77125761da06c842 + md5: e9d08c8f9a02853bfc6f8c3a4b0b19a4 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=15 + - ncurses >=6.6,<7.0a0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.8.1,<3.0a0 + - liblzma >=5.8.3,<6.0a0 + - libcurl >=8.21.0,<9.0a0 + - rhash >=1.4.6,<2.0a0 + - libuv >=1.52.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 26077721 + timestamp: 1787711119591 - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-15.3.0-h8846f6e_4.conda sha256: 87ebd4f94c8823763d35adaba292c2dc8c60ddda70e0980f32eae0dbb0e2aa2d md5: 4bfbca2f3bfcdf6d78a339cfd5368fce @@ -158,6 +220,36 @@ packages: - icu >=78.3,<79.0a0 size: 14459115 timestamp: 1786545741408 +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + sha256: dd053c96dcb0dcfd59422aefea9d2fe937a190167f34fba7893ec1e10a7e8963 + md5: ba55d1b89fd7775e67de8291029b4059 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: LGPL-2.1-or-later + run_exports: + weak: + - keyutils >=1.6.3,<2.0a0 + size: 135295 + timestamp: 1786739238128 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + sha256: 2a5c38c85e63df84c4e69ee71439841ce570d259ae3060627bb9a49a938d66f4 + md5: 53318d715316929a574f83591308b1f8 + depends: + - __glibc >=2.17,<3.0.a0 + - keyutils >=1.6.3,<2.0a0 + - libedit >=3.1.20250104,<3.2.0a0 + - libedit >=3.1.20250104,<4.0a0 + - libgcc >=15 + - libstdcxx >=15 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - krb5 >=1.22.2,<1.23.0a0 + size: 1394333 + timestamp: 1786762112514 - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda sha256: 27d83f1188cd19bcb7754a078b3fa7f4cfb8527f8eb2fde54dd01fc529d1adec md5: 449500f2c089da11c40f5c21312e3e07 @@ -171,6 +263,67 @@ packages: run_exports: {} size: 745303 timestamp: 1784214507189 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.22.0-ha042cf0_0.conda + sha256: 483b7eb97f56fbb3a9cb7190d33012dfa0a5aaed99ede61465563cd97dd82504 + md5: e617deee7af8eb0c04f6296d12b54157 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=15 + - libnghttp2 >=1.68.1,<2.0a0 + - libpsl >=0.23.1,<0.24.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.8,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + run_exports: + weak: + - libcurl >=8.22.0,<9.0a0 + size: 499651 + timestamp: 1788340719230 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda + sha256: 6473eb8caf2aae830f37caa93db9b26dddf7ac84b63229e8bf7fc0e5c3ab95b0 + md5: 50708d3b951d0f8e2d7f2df5b5edc040 + depends: + - ncurses + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - ncurses >=6.6,<7.0a0 + license: BSD-2-Clause + license_family: BSD + run_exports: + weak: + - libedit >=3.1.20250104,<3.2.0a0 + size: 135098 + timestamp: 1786616658086 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda + sha256: e4418f68d01a6307c4431b585c71b584f40189877364e542ee87deb777f5e85b + md5: 7bc31538d6e3fb349e897353969237ec + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: BSD-2-Clause OR GPL-2.0-or-later + license_family: GPL + run_exports: + weak: + - libev >=4.33,<4.34.0a0 + size: 43220 + timestamp: 1785917328200 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda + sha256: 16feffd9ddbbe5b718515d38ee376c685ba95491cd901244e24671d20b952a77 + md5: b24d3c612f71e7aa74158d92106318b2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - expat 2.8.1.* + license: MIT + license_family: MIT + run_exports: {} + size: 77856 + timestamp: 1781203599810 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda sha256: 24090e675d34403b4ee1cd4372d8f6c0937da7ecfd66a19a57cac2ed0f4ea793 md5: cba14d01083fc62ffd32c24d7d390633 @@ -197,6 +350,54 @@ packages: - _openmp_mutex >=4.5 size: 639968 timestamp: 1787618616266 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + sha256: 9787df8c22a59c9a70d3e5a10db9ad663485e75e9ccc3f09bd092cb7b95e0dab + md5: 1390b7c5ac0b1d8e447bc5efa6d3c8c2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - xz 5.8.3.* + license: 0BSD + run_exports: + weak: + - liblzma >=5.8.3,<6.0a0 + size: 112995 + timestamp: 1786348617826 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda + sha256: 11a2f54a6f391e25738bce0023e6ef499600147bbcf21c1fa69d2e251b03cc6a + md5: 75d211f04ac87506f1f43420712a69fe + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.8,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=15 + - libstdcxx >=15 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libnghttp2 >=1.68.1,<2.0a0 + size: 649974 + timestamp: 1787177490105 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda + sha256: 09e8effdc89bc5d021349318d32b85594f5b8d8e3ab8f90a85b81f8fe695a566 + md5: 77be60417aa572afcb48cbe0f967401d + depends: + - libstdcxx >=15 + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libpsl >=0.23.1,<0.24.0a0 + size: 72519 + timestamp: 1786970753847 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-15.3.0-h54950a5_4.conda sha256: 074869ef41874beec0ffee576fdd809f72eb8a4196fe2fa4d1a922c9a0742572 md5: 306fcd92c8f1cd7049bc6e361bd37f09 @@ -211,6 +412,21 @@ packages: - libsanitizer 15.3.0 size: 7579092 timestamp: 1787618435127 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda + sha256: 7e463000fa1cba3f3a6597b776f6ab301d425a96e3bc20d0d9d76a3b9f237aa3 + md5: 5c526561e1d2a2e071941174eae84557 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libssh2 >=1.11.1,<2.0a0 + size: 306045 + timestamp: 1786713107897 - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda sha256: 40b792b0186c1e8859280a1f6f19a54fc50a11b32724fc7b637009c1a9bd302b md5: 2f2ef0d96de5bdd8c1270ff22fdf9352 @@ -251,6 +467,18 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 63713 timestamp: 1785362952714 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + sha256: 5d46557214ed184381dafe835b7c94a474a1c3b307a08a250b1ea4779b44ffb3 + md5: ee6c0cd80a60961a1f48aa3e0b91f986 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: X11 AND BSD-3-Clause + run_exports: + weak: + - ncurses >=6.6,<7.0a0 + size: 911196 + timestamp: 1786355078102 - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-20.20.2-h4dda4d0_2.conda sha256: a3e0b1dd02b24e8959ab11f0f72c01fcbaea47d12a3d60b3289c8766da182fed md5: d49c2e5d7f0963290033007ccc2c69bb @@ -284,6 +512,19 @@ packages: - openssl >=3.6.4,<4.0a0 size: 3202955 timestamp: 1787698780103 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rhash-1.4.6-hb03c661_2.conda + sha256: 186a5b225077ad5f4656966da947d43307101950751ef4a239b455588756b4e1 + md5: 007602d697e07c11ff9864964eba5ee3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - rhash >=1.4.6,<2.0a0 + size: 194994 + timestamp: 1786731436520 - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda sha256: 16080a1c7724f7d25727cdc23c7658e0cec2db52448c1dc0c33467ee2c6e1c62 md5: 6acb86426229f96f93e5468d1df3a5e8 diff --git a/pixi.toml b/pixi.toml index 2ccd6cdacc..f3eb29df3b 100644 --- a/pixi.toml +++ b/pixi.toml @@ -21,6 +21,7 @@ version = "0.1.0" [feature.docs.dependencies] nodejs = "20.*" cxx-compiler = "*" +cmake = "*" [feature.docs.tasks] docs-build = "cd doc && bash build_antora.sh" From 166b77dc893d4260f1344ee469b79eff1db7a673 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Tue, 8 Sep 2026 21:39:52 +0200 Subject: [PATCH 34/37] Fix doc-comment issues and mrdocs warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 33 missing \brief, - 7 missing enum-value docs, - 7 stale/typo \param tags — including a genuine typo, sparsaefill → sparsefill, - and several copy-pasted \param blocks referencing parameters that don't exist in that particular overload. --- doc/mrdocs.yml | 1 + include/boost/gil/algorithm.hpp | 10 +++++----- include/boost/gil/channel.hpp | 2 ++ .../boost/gil/extension/dynamic_image/algorithm.hpp | 12 ++++++++++++ .../extension/dynamic_image/image_view_factory.hpp | 11 +++++++++++ .../gil/extension/image_processing/diffusion.hpp | 10 ++++++---- include/boost/gil/histogram.hpp | 2 +- include/boost/gil/image_processing/threshold.hpp | 6 ++++-- include/boost/gil/io/read_and_convert_image.hpp | 2 -- include/boost/gil/io/read_and_convert_view.hpp | 4 +--- include/boost/gil/io/read_view.hpp | 1 - include/boost/gil/metafunctions.hpp | 8 ++++++++ 12 files changed, 51 insertions(+), 18 deletions(-) diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index b97e1dcc83..b36283cff2 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -22,5 +22,6 @@ multipage: true sort-members: false base-url: https://github.com/boostorg/gil/blob/master/include/ use-system-libc: true +warn-as-error: true warn-if-undocumented: false warn-no-paramdoc: false diff --git a/include/boost/gil/algorithm.hpp b/include/boost/gil/algorithm.hpp index 428481af85..8cc4251895 100644 --- a/include/boost/gil/algorithm.hpp +++ b/include/boost/gil/algorithm.hpp @@ -1419,11 +1419,11 @@ void view_multiplies_scalar(SrcView const& src_view, Scalar const& scalar, DstVi /// \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 + 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 diff --git a/include/boost/gil/channel.hpp b/include/boost/gil/channel.hpp index 44ae58568a..9e81c5790f 100644 --- a/include/boost/gil/channel.hpp +++ b/include/boost/gil/channel.hpp @@ -475,6 +475,7 @@ class packed_channel_reference_base /// assert(data == 6); // == 3<<1 == 6 /// \endcode +/// \brief Represents a reference proxy to a channel operating over a bit range whose offset is fixed at compile time. Models ChannelConcept /// \tparam BitField A type that holds the bits of the pixel from which the channel is referenced. Typically an integral type, like std::uint16_t /// \tparam FirstBit Defines, with NumBits, the sequence of bits in the data value that contain the channel /// \tparam NumBits Defines, with FirstBit, the sequence of bits in the data value that contain the channel @@ -482,6 +483,7 @@ class packed_channel_reference_base template class packed_channel_reference; +/// \brief Represents a reference proxy to a channel operating over a bit range whose offset is specified at run time. Models ChannelConcept /// \tparam BitField A type that holds the bits of the pixel from which the channel is referenced. Typically an integral type, like std::uint16_t /// \tparam NumBits Defines the sequence of bits in the data value that contain the channel /// \tparam IsMutable true if the reference is mutable diff --git a/include/boost/gil/extension/dynamic_image/algorithm.hpp b/include/boost/gil/extension/dynamic_image/algorithm.hpp index 473b2b4c0b..9ad19532d0 100644 --- a/include/boost/gil/extension/dynamic_image/algorithm.hpp +++ b/include/boost/gil/extension/dynamic_image/algorithm.hpp @@ -44,6 +44,7 @@ struct equal_pixels_fn : binary_operation_obj } // namespace detail /// \ingroup ImageViewSTLAlgorithmsEqualPixels +/// \brief Compares a run-time typed image view to a statically-typed view for equality. /// \tparam Types Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam View Model MutableImageViewConcept template @@ -55,6 +56,7 @@ auto equal_pixels(any_image_view const& src, View const& dst) -> bool } /// \ingroup ImageViewSTLAlgorithmsEqualPixels +/// \brief Compares a statically-typed image view to a run-time typed view for equality. /// \tparam View Model ImageViewConcept /// \tparam Types Model Boost.MP11-compatible list of models of MutableImageViewConcept template @@ -66,6 +68,7 @@ auto equal_pixels(View const& src, any_image_view const& dst) -> bool } /// \ingroup ImageViewSTLAlgorithmsEqualPixels +/// \brief Compares two run-time typed image views for equality. /// \tparam Types1 Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam Types2 Model Boost.MP11-compatible list of models of MutableImageViewConcept template @@ -89,6 +92,7 @@ struct copy_pixels_fn : public binary_operation_obj } // namespace detail /// \ingroup ImageViewSTLAlgorithmsCopyPixels +/// \brief Copies pixels from a run-time typed image view into a statically-typed view. /// \tparam Types Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam View Model MutableImageViewConcept template @@ -98,6 +102,7 @@ void copy_pixels(any_image_view const& src, View const& dst) } /// \ingroup ImageViewSTLAlgorithmsCopyPixels +/// \brief Copies pixels from a statically-typed image view into a run-time typed view. /// \tparam Types Model Boost.MP11-compatible list of models of MutableImageViewConcept /// \tparam View Model ImageViewConcept template @@ -107,6 +112,7 @@ void copy_pixels(View const& src, any_image_view const& dst) } /// \ingroup ImageViewSTLAlgorithmsCopyPixels +/// \brief Copies pixels between two run-time typed image views. /// \tparam Types1 Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam Types2 Model Boost.MP11-compatible list of models of MutableImageViewConcept template @@ -119,6 +125,7 @@ void copy_pixels(any_image_view const& src, any_image_view struct default_color_converter; /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels +/// \brief Copies and color-converts pixels from a run-time typed image view into a statically-typed view, using the given color converter. /// \tparam Types Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam View Model MutableImageViewConcept /// \tparam CC Model ColorConverterConcept @@ -130,6 +137,7 @@ void copy_and_convert_pixels(any_image_view const& src, View const& ds } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels +/// \brief Copies and color-converts pixels from a run-time typed image view into a statically-typed view, using the default color converter. /// \tparam Types Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam View Model MutableImageViewConcept template @@ -140,6 +148,7 @@ void copy_and_convert_pixels(any_image_view const& src, View const& ds } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels +/// \brief Copies and color-converts pixels from a statically-typed image view into a run-time typed view, using the given color converter. /// \tparam View Model ImageViewConcept /// \tparam Types Model Boost.MP11-compatible list of models of MutableImageViewConcept /// \tparam CC Model ColorConverterConcept @@ -151,6 +160,7 @@ void copy_and_convert_pixels(View const& src, any_image_view const& ds } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels +/// \brief Copies and color-converts pixels from a statically-typed image view into a run-time typed view, using the default color converter. /// \tparam View Model ImageViewConcept /// \tparam Types Model Boost.MP11-compatible list of models of MutableImageViewConcept template @@ -161,6 +171,7 @@ void copy_and_convert_pixels(View const& src, any_image_view const& ds } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels +/// \brief Copies and color-converts pixels between two run-time typed image views, using the given color converter. /// \tparam Types1 Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam Types2 Model Boost.MP11-compatible list of models of MutableImageViewConcept /// \tparam CC Model ColorConverterConcept @@ -173,6 +184,7 @@ void copy_and_convert_pixels( } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels +/// \brief Copies and color-converts pixels between two run-time typed image views, using the default color converter. /// \tparam Types1 Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam Types2 Model Boost.MP11-compatible list of models of MutableImageViewConcept template 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 3d01724d36..964e86f0c8 100644 --- a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp +++ b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp @@ -167,6 +167,7 @@ struct color_converted_view_fn } // namespace detail /// \ingroup ImageViewTransformationsFlipUD +/// \brief Returns a runtime-typed view of a runtime source image view, flipped vertically. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -178,6 +179,7 @@ auto flipped_up_down_view(any_image_view const& src) } /// \ingroup ImageViewTransformationsFlipLR +/// \brief Returns a runtime-typed view of a runtime source image view, flipped horizontally. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -189,6 +191,7 @@ auto flipped_left_right_view(any_image_view const& src) } /// \ingroup ImageViewTransformationsTransposed +/// \brief Returns a runtime-typed transposed view of a runtime source image view. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -200,6 +203,7 @@ auto transposed_view(any_image_view const& src) } /// \ingroup ImageViewTransformations90CW +/// \brief Returns a runtime-typed view of a runtime source image view, rotated 90 degrees clockwise. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -211,6 +215,7 @@ auto rotated90cw_view(any_image_view const& src) } /// \ingroup ImageViewTransformations90CCW +/// \brief Returns a runtime-typed view of a runtime source image view, rotated 90 degrees counterclockwise. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -222,6 +227,7 @@ auto rotated90ccw_view(any_image_view const& src) } /// \ingroup ImageViewTransformations180 +/// \brief Returns a runtime-typed view of a runtime source image view, rotated 180 degrees. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -233,6 +239,7 @@ auto rotated180_view(any_image_view const& src) } /// \ingroup ImageViewTransformationsSubimage +/// \brief Returns a runtime-typed view over a rectangular subimage of a runtime source image view. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -247,6 +254,7 @@ auto subimage_view( } /// \ingroup ImageViewTransformationsSubimage +/// \brief Returns a runtime-typed view over a rectangular subimage of a runtime source image view. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -260,6 +268,7 @@ auto subimage_view( } /// \ingroup ImageViewTransformationsSubsampled +/// \brief Returns a runtime-typed subsampled view of a runtime source image view. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -272,6 +281,7 @@ auto subsampled_view(any_image_view const& src, point_t const& step) } /// \ingroup ImageViewTransformationsSubsampled +/// \brief Returns a runtime-typed subsampled view of a runtime source image view. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline @@ -302,6 +312,7 @@ struct nth_channel_view_type> }; /// \ingroup ImageViewTransformationsNthChannel +/// \brief Returns a runtime-typed view over a single channel of a runtime source image view. /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline diff --git a/include/boost/gil/extension/image_processing/diffusion.hpp b/include/boost/gil/extension/image_processing/diffusion.hpp index b8333c6291..9ac7f06e72 100644 --- a/include/boost/gil/extension/image_processing/diffusion.hpp +++ b/include/boost/gil/extension/image_processing/diffusion.hpp @@ -269,16 +269,18 @@ struct rgb_luminance } // namespace brightness_function +/// \brief Pixel connectivity used by MATLAB-compatible anisotropic diffusion enum class matlab_connectivity { - minimal, - maximal + minimal, ///< 4-connectivity (N, S, E, W neighbors only) + maximal ///< 8-connectivity (including diagonal neighbors) }; +/// \brief Conduction (edge-stopping) function used by MATLAB-compatible anisotropic diffusion enum class matlab_conduction_method { - exponential, - quadratic + exponential, ///< exponential conduction function, favors high-contrast edges + quadratic ///< quadratic conduction function, favors wide regions over smaller ones }; template diff --git a/include/boost/gil/histogram.hpp b/include/boost/gil/histogram.hpp index 9012089541..7dd63cc22f 100644 --- a/include/boost/gil/histogram.hpp +++ b/include/boost/gil/histogram.hpp @@ -614,7 +614,7 @@ void fill_histogram(SrcView const&, Container&); /// @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 sparsefill 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) diff --git a/include/boost/gil/image_processing/threshold.hpp b/include/boost/gil/image_processing/threshold.hpp index 041df38ace..38987b3fc2 100644 --- a/include/boost/gil/image_processing/threshold.hpp +++ b/include/boost/gil/image_processing/threshold.hpp @@ -88,10 +88,12 @@ enum class threshold_truncate_mode zero ///< \todo TODO }; +/// \ingroup ImageProcessing +/// \brief Method used to compute the local threshold for adaptive thresholding. enum class threshold_adaptive_method { - mean, - gaussian + mean, ///< local threshold is the unweighted mean of the neighborhood + gaussian ///< local threshold is a Gaussian-weighted sum of the neighborhood }; /// \ingroup ImageProcessing diff --git a/include/boost/gil/io/read_and_convert_image.hpp b/include/boost/gil/io/read_and_convert_image.hpp index 4e1488c5c9..bef8a09c5d 100644 --- a/include/boost/gil/io/read_and_convert_image.hpp +++ b/include/boost/gil/io/read_and_convert_image.hpp @@ -24,8 +24,6 @@ namespace boost{ namespace gil { /// \brief Reads and color-converts an image. Image memory is allocated. /// \param reader An image reader. /// \param img The image in which the data is read into. -/// \param settings Specifies read settings depending on the image format. -/// \param cc Color converter function object. /// \throw std::ios_base::failure template inline diff --git a/include/boost/gil/io/read_and_convert_view.hpp b/include/boost/gil/io/read_and_convert_view.hpp index d9f9a99ebc..228ea21c08 100644 --- a/include/boost/gil/io/read_and_convert_view.hpp +++ b/include/boost/gil/io/read_and_convert_view.hpp @@ -23,9 +23,7 @@ namespace boost{ namespace gil { /// \brief Reads and color-converts an image view. No memory is allocated. /// \param reader An image reader. -/// \param img The image in which the data is read into. -/// \param settings Specifies read settings depending on the image format. -/// \param cc Color converter function object. +/// \param view The image view in which the data is read into. /// \throw std::ios_base::failure template inline diff --git a/include/boost/gil/io/read_view.hpp b/include/boost/gil/io/read_view.hpp index 813dfa4bdb..84850dc452 100644 --- a/include/boost/gil/io/read_view.hpp +++ b/include/boost/gil/io/read_view.hpp @@ -24,7 +24,6 @@ namespace boost { namespace gil { /// \brief Reads an image view without conversion. No memory is allocated. /// \param reader An image reader. /// \param view The image view in which the data is read into. -/// \param settings Specifies read settings depending on the image format. /// \throw std::ios_base::failure template inline diff --git a/include/boost/gil/metafunctions.hpp b/include/boost/gil/metafunctions.hpp index 092f867ae5..5b378a2d0a 100644 --- a/include/boost/gil/metafunctions.hpp +++ b/include/boost/gil/metafunctions.hpp @@ -77,36 +77,44 @@ struct pixel_reference_is_basic> : std::tru template struct iterator_is_basic : std::false_type {}; +/// \brief Specialization for a mutable interleaved pixel iterator. /// \tparam T mutable interleaved pixel type template struct iterator_is_basic*> : std::true_type {}; +/// \brief Specialization for an immutable interleaved pixel iterator. /// \tparam T immutable interleaved pixel type template struct iterator_is_basic const*> : std::true_type {}; +/// \brief Specialization for a mutable planar pixel iterator. /// \tparam T mutable planar pixel type template struct iterator_is_basic> : std::true_type {}; +/// \brief Specialization for an immutable planar pixel iterator. /// \tparam T immutable planar pixel type template struct iterator_is_basic> : std::true_type {}; +/// \brief Specialization for a mutable interleaved step iterator. /// \tparam T mutable interleaved step template struct iterator_is_basic*>> : std::true_type {}; +/// \brief Specialization for an immutable interleaved step iterator. /// \tparam T immutable interleaved step template struct iterator_is_basic const*>> : std::true_type {}; +/// \brief Specialization for a mutable planar step iterator. /// \tparam T mutable planar step template struct iterator_is_basic>> : std::true_type {}; +/// \brief Specialization for an immutable planar step iterator. /// \tparam T immutable planar step template struct iterator_is_basic>> From cd2d21af4558cf9cf43b8909039851a2c2373aeb Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 9 Sep 2026 11:49:32 +0200 Subject: [PATCH 35/37] ci: Fix docs-config.sh pointing to the old build/site folder --- .github/actions/generate-doc/docs-config.sh | 4 ++-- doc/build_antora.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/generate-doc/docs-config.sh b/.github/actions/generate-doc/docs-config.sh index 1731c2dee0..19076536fe 100644 --- a/.github/actions/generate-doc/docs-config.sh +++ b/.github/actions/generate-doc/docs-config.sh @@ -18,12 +18,12 @@ if [ "${GITHUB_REF##*/}" = develop ]; then rm -rf develop mkdir -p develop/doc cp ../index.html develop/ - cp -a ../doc/build/site/. develop/doc/ + cp -a ../doc/html/. develop/doc/ else # main branch rm -f index.html rm -rf html gil - cp -a ../doc/build/site/. . + cp -a ../doc/html/. . fi # Remove version control diff --git a/doc/build_antora.sh b/doc/build_antora.sh index 593d3be17b..8986946e94 100644 --- a/doc/build_antora.sh +++ b/doc/build_antora.sh @@ -13,7 +13,7 @@ cd "$SCRIPT_DIR" # Required by our CMake. # Prevents Antora from cloning Boost again -export BOOST_SRC_DIR=$(realpath $SCRIPT_DIR/../../..) +export BOOST_SRC_DIR=${BOOST_SRC_DIR:-$(realpath $SCRIPT_DIR/../../..)} npm ci npx antora --log-format=pretty --stacktrace --log-level info gil-playbook.yml From 4edaf5d197b5866092e194a0c3b7eb57d4bb0e56 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 9 Sep 2026 17:56:16 +0200 Subject: [PATCH 36/37] ci: Fix antora 'Corpus is empty' --- .github/actions/generate-doc/action.yml | 13 +++++-------- .github/actions/setup-boost/action.yml | 2 +- .github/workflows/docs.yaml | 2 +- doc/build_antora.sh | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/actions/generate-doc/action.yml b/.github/actions/generate-doc/action.yml index dff8544db2..55e5d55b95 100644 --- a/.github/actions/generate-doc/action.yml +++ b/.github/actions/generate-doc/action.yml @@ -4,14 +4,11 @@ runs: using: composite steps: - run: | - # Antora's local content source (doc/local-playbook.yml: `url: ..`) - # must be a real git repository. setup-boost's copy into - # ../boost-root/libs/gil (via `cp -r`) drops .git, so the build must - # run from the original checkout ($GITHUB_WORKSPACE) -- only - # BOOST_SRC_DIR (cross-library headers for MrDocs) comes from - # ../boost-root. - export BOOST_SRC_DIR="$(cd ../boost-root && pwd)" - cd doc + # setup-boost copies the checkout (with .git, via `cp -a`) into + # ../boost-root/libs/gil, so doc/ is nested under a real Boost + # superproject there -- needed by build_antora.sh's BOOST_SRC_DIR + # detection and by Antora's local content source. + cd ../boost-root/libs/gil/doc bash build_antora.sh cd .. chmod +x $GITHUB_WORKSPACE/.github/actions/generate-doc/docs-config.sh diff --git a/.github/actions/setup-boost/action.yml b/.github/actions/setup-boost/action.yml index 89ba925f37..903fe48500 100644 --- a/.github/actions/setup-boost/action.yml +++ b/.github/actions/setup-boost/action.yml @@ -9,7 +9,7 @@ runs: 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 + cp -a $GITHUB_WORKSPACE/. libs/gil/ git submodule update --init tools/boostdep python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" gil ./bootstrap.sh diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index a75ab73e3a..e19faae4f0 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -19,6 +19,6 @@ jobs: uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: temp-doc + publish_dir: ../boost-root/libs/gil/temp-doc user_name: 'github-actions[bot]' user_email: 'github-actions[bot]@users.noreply.github.com' diff --git a/doc/build_antora.sh b/doc/build_antora.sh index 8986946e94..593d3be17b 100644 --- a/doc/build_antora.sh +++ b/doc/build_antora.sh @@ -13,7 +13,7 @@ cd "$SCRIPT_DIR" # Required by our CMake. # Prevents Antora from cloning Boost again -export BOOST_SRC_DIR=${BOOST_SRC_DIR:-$(realpath $SCRIPT_DIR/../../..)} +export BOOST_SRC_DIR=$(realpath $SCRIPT_DIR/../../..) npm ci npx antora --log-format=pretty --stacktrace --log-level info gil-playbook.yml From 9efa2df8a012c052c13c5f89bf037670b5f85824 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Wed, 9 Sep 2026 22:11:12 +0200 Subject: [PATCH 37/37] Fix index.html redirections --- doc/index.html | 24 ++++++++++++++++++++++++ index.html | 29 ++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 doc/index.html diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000000..7c201d631a --- /dev/null +++ b/doc/index.html @@ -0,0 +1,24 @@ + + + + Boost.GIL + + + + + Automatic redirection failed, please go to + ./html/gil/index.html +


+ + Boost.GIL
+
+ Copyright (C) 2026 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)
+
+
+ + + \ No newline at end of file diff --git a/index.html b/index.html index d4ecb1ad8f..bb9f658c30 100644 --- a/index.html +++ b/index.html @@ -1,9 +1,24 @@ - - - - + + + Boost.GIL + + + + Automatic redirection failed, please go to - doc/index.html - - + ./doc/html/gil/index.html +
+ + Boost.GIL
+
+ Copyright (C) 2026 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)
+
+
+ + + \ No newline at end of file