Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reflection option for boundary handling in 1D correlation #613

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions include/boost/gil/algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1416,11 +1416,12 @@ 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
extend_reflection /// assumes boundary values as reflection of source row pixels
};

namespace detail
Expand Down
13 changes: 13 additions & 0 deletions include/boost/gil/image_processing/convolve.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2005-2007 Adobe Systems Incorporated
// Copyright 2019 Miral Shah <[email protected]>
// Copyright 2019-2021 Pranam Lashkari <[email protected]>
// Copyright 2021 Prathamesh Tagore <[email protected]>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
Expand Down Expand Up @@ -143,6 +144,18 @@ void correlate_rows_impl(
pixel_assigns_t<src_pixel_ref_t, PixelAccum>()(src_view.row_end(y)[-1], filler);
std::fill_n(it_buffer, kernel.right_size(), filler);
}
else if (option == boundary_option::extend_reflection)
{
assign_pixels(src_view.row_begin(y), src_view.row_begin(y) + kernel.left_size(),
it_buffer);
std::reverse(buffer.begin(), buffer.begin() + kernel.left_size());
it_buffer += kernel.left_size();
assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer);
it_buffer += width;
assign_pixels(src_view.row_end(y) - kernel.right_size(), src_view.row_end(y),
it_buffer);
std::reverse(buffer.end() - kernel.right_size(), buffer.end());
}

correlator(
&buffer.front(), &buffer.front() + width,
Expand Down
70 changes: 70 additions & 0 deletions test/core/image_processing/convolve.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//
// Copyright 2019-2020 Mateusz Loskot <mateusz at loskot dot net>
// Copyright 2021 Pranam Lashkari <[email protected]>
// Copyright 2021 Prathamesh Tagore <[email protected]>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
Expand Down Expand Up @@ -111,12 +112,81 @@ struct test_image_5x5_kernel_3x3_identity
}
};

struct test_image_5x5_kernel_1x9_boundary_extend_reflection
{
template <typename Image>
void operator()(Image const&)
{
using image_t = Image;
using pixel_t = typename image_t::value_type;
using channel_t = typename gil::channel_type<pixel_t>::type;
auto img = fixture::generate_image<image_t>(5, 5, fixture::random_value<channel_t>{});
auto img_view = gil::view(img);
image_t img_out_up_left_offset(img), img_expected_row_up_offset(img);
image_t img_expected_col_left_offset(img), img_out_down_right_offset(img);
image_t img_expected_row_down_offset(img), img_expected_col_right_offset(img);
int kernel_shift_up_left_offset = 2, kernel_shift_down_right_offset = -2;

fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_row_up_offset),
kernel_shift_up_left_offset);
fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_row_up_offset),
-1, 0, 1, img_view.height(), 1);
fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_row_up_offset),
1, 0, 0, img_view.height(), 2);

fixture::col_conv1D_offset_img_generator(gil::view(img_expected_row_up_offset),
gil::view(img_expected_col_left_offset), kernel_shift_up_left_offset);
fixture::col_conv1D_offset_img_generator(gil::view(img_expected_row_up_offset),
gil::view(img_expected_col_left_offset), -1, 1, 0, 1, img_view.width());
fixture::col_conv1D_offset_img_generator(gil::view(img_expected_row_up_offset),
gil::view(img_expected_col_left_offset), 1, 0, 0, 2, img_view.width());

fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_row_down_offset),
kernel_shift_down_right_offset, 0, 2, img_view.height(), img_view.width());
fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_row_down_offset),
-1, 0, img_view.width() - 1, img_view.height(), img_view.width());
fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_row_down_offset),
1, 0, img_view.width() - 2, img_view.height(), img_view.width());

fixture::col_conv1D_offset_img_generator(gil::view(img_expected_row_down_offset),
gil::view(img_expected_col_right_offset), kernel_shift_down_right_offset, 2, 0,
img_view.height(), img_view.width());
fixture::col_conv1D_offset_img_generator(gil::view(img_expected_row_down_offset),
gil::view(img_expected_col_right_offset), -1, img_view.height() - 1, 0,
img_view.height(), img_view.width());
fixture::col_conv1D_offset_img_generator(gil::view(img_expected_row_down_offset),
gil::view(img_expected_col_right_offset), 1, img_view.height() - 2, 0,
img_view.height(), img_view.width());

auto const kernel_up_left_offset = fixture::create_kernel<channel_t>(
{0, 0, 0, 0, 0, 0, 1, 0, 0});
gil::detail::convolve_1d<pixel_t>(gil::const_view(img), kernel_up_left_offset,
gil::view(img_out_up_left_offset), gil::boundary_option::extend_reflection);

auto const kernel_down_right_offset = fixture::create_kernel<channel_t>(
{0, 0, 1, 0, 0, 0, 0, 0, 0});
gil::detail::convolve_1d<pixel_t>(gil::const_view(img), kernel_down_right_offset,
gil::view(img_out_down_right_offset), gil::boundary_option::extend_reflection);

BOOST_TEST(gil::equal_pixels(gil::const_view(img_out_up_left_offset),
gil::const_view(img_expected_col_left_offset)));
BOOST_TEST(gil::equal_pixels(gil::const_view(img_out_down_right_offset),
gil::const_view(img_expected_col_right_offset)));
}
static void run()
{
boost::mp11::mp_for_each<fixture::image_types>(
test_image_5x5_kernel_1x9_boundary_extend_reflection{});
}
};

int main()
{
test_image_1x1_kernel_1x1_identity::run();
test_image_1x1_kernel_3x3_identity::run();
test_image_3x3_kernel_3x3_identity::run();
test_image_5x5_kernel_3x3_identity::run();

test_image_5x5_kernel_1x9_boundary_extend_reflection::run();
return ::boost::report_errors();
}
52 changes: 52 additions & 0 deletions test/core/image_processing/convolve_cols.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//
// Copyright 2019-2020 Mateusz Loskot <mateusz at loskot dot net>
// Copyright 2021 Pranam Lashkari <[email protected]>
// Copyright 2021 Prathamesh Tagore <[email protected]>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
Expand Down Expand Up @@ -65,10 +66,61 @@ struct test_image_1x1_kernel_3x3_identity
}
};

struct test_image_5x5_kernel_1x9_boundary_extend_reflection
{
template <typename Image>
void operator()(Image const&)
{
using image_t = Image;
using pixel_t = typename image_t::value_type;
using channel_t = typename gil::channel_type<pixel_t>::type;
auto img = fixture::generate_image<image_t>(5, 5, fixture::random_value<channel_t>{});
auto img_view = gil::view(img);
image_t img_out_left_offset(img), img_expected_left_offset(img);
image_t img_out_right_offset(img), img_expected_right_offset(img);
int kernel_shift_left_offset = 2, kernel_shift_right_offset = -2;

fixture::col_conv1D_offset_img_generator(img_view, gil::view(img_expected_left_offset),
kernel_shift_left_offset);
fixture::col_conv1D_offset_img_generator(img_view, gil::view(img_expected_left_offset),
-1, 1, 0, 1, img_view.width());
fixture::col_conv1D_offset_img_generator(img_view, gil::view(img_expected_left_offset),
1, 0, 0, 2, img_view.width());

fixture::col_conv1D_offset_img_generator(img_view, gil::view(img_expected_right_offset),
kernel_shift_right_offset, 2, 0, img_view.height(), img_view.width());
fixture::col_conv1D_offset_img_generator(img_view, gil::view(img_expected_right_offset),
-1, img_view.height() - 1, 0, img_view.height(), img_view.width());
fixture::col_conv1D_offset_img_generator(img_view, gil::view(img_expected_right_offset),
1, img_view.height() - 2, 0, img_view.height(), img_view.width());

auto const kernel_left_offset = fixture::create_kernel<channel_t>(
{0, 0, 0, 0, 0, 0, 1, 0, 0});
gil::convolve_cols<pixel_t>(gil::const_view(img), kernel_left_offset,
gil::view(img_out_left_offset), gil::boundary_option::extend_reflection);

auto const kernel_right_offset = fixture::create_kernel<channel_t>(
{0, 0, 1, 0, 0, 0, 0, 0, 0});
gil::convolve_cols<pixel_t>(gil::const_view(img), kernel_right_offset,
gil::view(img_out_right_offset), gil::boundary_option::extend_reflection);

BOOST_TEST(gil::equal_pixels(gil::const_view(img_out_left_offset),
gil::const_view(img_expected_left_offset)));
BOOST_TEST(gil::equal_pixels(gil::const_view(img_out_right_offset),
gil::const_view(img_expected_right_offset)));
}
static void run()
{
boost::mp11::mp_for_each<fixture::image_types>(
test_image_5x5_kernel_1x9_boundary_extend_reflection{});
}
};

int main()
{
test_image_1x1_kernel_1x1_identity::run();
test_image_1x1_kernel_3x3_identity::run();

test_image_5x5_kernel_1x9_boundary_extend_reflection::run();
return ::boost::report_errors();
}
52 changes: 52 additions & 0 deletions test/core/image_processing/convolve_rows.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//
// Copyright 2019-2020 Mateusz Loskot <mateusz at loskot dot net>
// Copyright 2021 Pranam Lashkari <[email protected]>
// Copyright 2021 Prathamesh Tagore <[email protected]>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
Expand Down Expand Up @@ -65,10 +66,61 @@ struct test_image_1x1_kernel_3x3_identity
}
};

struct test_image_5x5_kernel_1x9_boundary_extend_reflection
{
template <typename Image>
void operator()(Image const&)
{
using image_t = Image;
using pixel_t = typename image_t::value_type;
using channel_t = typename gil::channel_type<pixel_t>::type;
auto img = fixture::generate_image<image_t>(5, 5, fixture::random_value<channel_t>{});
auto img_view = gil::view(img);
image_t img_out_up_offset(img), img_expected_up_offset(img);
image_t img_out_down_offset(img), img_expected_down_offset(img);
int kernel_shift_up_offset = 2, kernel_shift_down_offset = -2;

fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_up_offset),
kernel_shift_up_offset);
fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_up_offset),
-1, 0, 1, img_view.height(), 1);
fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_up_offset),
1, 0, 0, img_view.height(), 2);

fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_down_offset),
kernel_shift_down_offset, 0, 2, img_view.height(), img_view.width());
fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_down_offset),
-1, 0, img_view.width() - 1, img_view.height(), img_view.width());
fixture::row_conv1D_offset_img_generator(img_view, gil::view(img_expected_down_offset),
1, 0, img_view.width() - 2, img_view.height(), img_view.width());

auto const kernel_up_offset = fixture::create_kernel<channel_t>(
{0, 0, 0, 0, 0, 0, 1, 0, 0});
gil::convolve_rows<pixel_t>(gil::const_view(img), kernel_up_offset,
gil::view(img_out_up_offset), gil::boundary_option::extend_reflection);

auto const kernel_down_offset = fixture::create_kernel<channel_t>(
{0, 0, 1, 0, 0, 0, 0, 0, 0});
gil::convolve_rows<pixel_t>(gil::const_view(img), kernel_down_offset,
gil::view(img_out_down_offset), gil::boundary_option::extend_reflection);

BOOST_TEST(gil::equal_pixels(gil::const_view(img_out_up_offset),
gil::const_view(img_expected_up_offset)));
BOOST_TEST(gil::equal_pixels(gil::const_view(img_out_down_offset),
gil::const_view(img_expected_down_offset)));
}
static void run()
{
boost::mp11::mp_for_each<fixture::image_types>(
test_image_5x5_kernel_1x9_boundary_extend_reflection{});
}
};

int main()
{
test_image_1x1_kernel_1x1_identity::run();
test_image_1x1_kernel_3x3_identity::run();

test_image_5x5_kernel_1x9_boundary_extend_reflection::run();
return ::boost::report_errors();
}
55 changes: 55 additions & 0 deletions test/core/image_processing/test_fixture.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//
// Copyright 2019 Mateusz Loskot <mateusz at loskot dot net>
// Copyright 2021 Pranam Lashkari <[email protected]>
// Copyright 2021 Prathamesh Tagore <[email protected]>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
Expand Down Expand Up @@ -28,4 +29,58 @@ auto create_kernel(std::initializer_list<T> const& values)
return kernel;
}

// Adds an offset similar to 1D row convolution with kernel {0, 0, 0, 0, 0, 0, 1, 0, 0}
// (for offset = 2) having its anchor point at 5th element and boundary option as "extend_zero".
template <typename SrcView, typename DstView>
void row_conv1D_offset_img_generator(SrcView src_view, DstView dst_view, int const offset,
std::ptrdiff_t start_row = 0, std::ptrdiff_t start_col = 0, std::ptrdiff_t end_row = -1,
std::ptrdiff_t end_col = -1)
{
BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
static_assert(color_spaces_are_compatible
<
typename color_space_type<SrcView>::type,
typename color_space_type<DstView>::type
>::value, "Source and destination views must have pixels with the same color space");

if (end_row == -1)
end_row = src_view.height();
if (end_col == -1)
end_col = src_view.width();
for (std::ptrdiff_t y = start_row; y < end_row; ++y)
{
auto src_it = src_view.row_begin(y);
auto dst_it = dst_view.row_begin(y);
for (std::ptrdiff_t x = offset + start_col; x < end_col; ++x)
dst_it[x] = src_it[x - offset];
}
}

// Adds an offset similar to 1D column convolution with kernel {0, 0, 0, 0, 0, 0, 1, 0, 0}
// (for offset = 2) having its anchor point at 5th element and boundary option as "extend_zero".
template <typename SrcView, typename DstView>
void col_conv1D_offset_img_generator(SrcView src_view, DstView dst_view, int const offset,
std::ptrdiff_t start_row = 0, std::ptrdiff_t start_col = 0, std::ptrdiff_t end_row = -1,
std::ptrdiff_t end_col = -1)
{
BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
static_assert(color_spaces_are_compatible
<
typename color_space_type<SrcView>::type,
typename color_space_type<DstView>::type
>::value, "Source and destination views must have pixels with the same color space");

if (end_row == -1)
end_row = src_view.height();
if (end_col == -1)
end_col = src_view.width();
for (std::ptrdiff_t x = start_col; x < end_col; ++x)
{
auto src_it = src_view.col_begin(x);
auto dst_it = dst_view.col_begin(x);
for (std::ptrdiff_t y = offset + start_row; y < end_row; ++y)
dst_it[y] = src_it[y - offset];
}
}

}}}} // namespace boost::gil::test::fixture