Skip to content

Commit

Permalink
Image boundary extension algorithm added
Browse files Browse the repository at this point in the history
  • Loading branch information
lpranam committed Sep 4, 2019
1 parent bd00f91 commit c46f31e
Show file tree
Hide file tree
Showing 3 changed files with 347 additions and 1 deletion.
137 changes: 137 additions & 0 deletions include/boost/gil/extension/numeric/algorithm.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// Copyright 2005-2007 Adobe Systems Incorporated
// Copyright 2019 Pranam Lashkari <plashkari628@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
Expand All @@ -12,6 +13,7 @@

#include <boost/gil/metafunctions.hpp>
#include <boost/gil/pixel_iterator.hpp>
#include <boost/gil/image.hpp>

#include <boost/assert.hpp>

Expand Down Expand Up @@ -248,6 +250,141 @@ void view_multiplies_scalar(SrcView const& src_view, Scalar const& scalar, DstVi
}
}


/// \ingroup ImageAlgorithms
/// \brief Boundary options for image boundary extension
enum class boundary_option
{
extend_zero, /// assume the source boundaries to be zero
extend_constant /// assume the source boundaries to be the boundary value
};

namespace detail
{

template <typename SrcView, typename RltView>
void extend_row_impl(
SrcView src_view,
RltView result_view,
std::size_t extend_count,
boundary_option option)
{
std::ptrdiff_t extend_count_ = static_cast<std::ptrdiff_t>(extend_count);

if (option == boundary_option::extend_constant)
{
for (std::ptrdiff_t i = 0; i < result_view.height(); i++)
{
if(i >= extend_count_ && i < extend_count_ + src_view.height())
{
assign_pixels(
src_view.row_begin(i - extend_count_),
src_view.row_end(i - extend_count_),
result_view.row_begin(i)
);
}
else if(i < extend_count_)
{
assign_pixels(src_view.row_begin(0), src_view.row_end(0), result_view.row_begin(i));
}
else
{
assign_pixels(
src_view.row_begin(src_view.height() - 1),
src_view.row_end(src_view.height() - 1),
result_view.row_begin(i)
);
}

}
}
else if (option == boundary_option::extend_zero)
{
typename SrcView::value_type acc_zero;
pixel_zeros_t<typename SrcView::value_type>()(acc_zero);

for (std::ptrdiff_t i = 0; i < result_view.height(); i++)
{
if (i >= extend_count_ && i < extend_count_ + src_view.height())
{
assign_pixels(
src_view.row_begin(i - extend_count_),
src_view.row_end(i - extend_count_),
result_view.row_begin(i));
}
else
{
std::fill_n(result_view.row_begin(i), result_view.width(), acc_zero);
}
}
}
}

} //namespace detail


/// \brief adds new row at top and bottom.
/// Image padding introduces new pixels around the edges of an image.
/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques.
/// \tparam SrcView Models ImageViewConcept
/// \tparam extend_count number of rows to be added each side
/// \tparam option - TODO
template <typename SrcView>
auto extend_row(
SrcView src_view,
std::size_t extend_count,
boundary_option option
) -> typename gil::image<typename SrcView::value_type>
{
typename gil::image<typename SrcView::value_type>
result_img(src_view.width(), src_view.height() + (2 * extend_count));

auto result_view = view(result_img);
detail::extend_row_impl(src_view, result_view, extend_count, option);
return result_img;
}


/// \brief adds new column at left and right.
/// Image padding introduces new pixels around the edges of an image.
/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques.
/// \tparam SrcView Models ImageViewConcept
/// \tparam extend_count number of columns to be added each side
/// \tparam option - TODO
template <typename SrcView>
auto extend_col(
SrcView src_view,
std::size_t extend_count,
boundary_option option
) -> typename gil::image<typename SrcView::value_type>
{
auto src_view_rotate = rotated90cw_view(src_view);

typename gil::image<typename SrcView::value_type>
result_img(src_view.width() + (2 * extend_count), src_view.height());

auto result_view = rotated90cw_view(view(result_img));
detail::extend_row_impl(src_view_rotate, result_view, extend_count, option);
return result_img;
}

/// \brief adds new row and column at all sides.
/// Image padding introduces new pixels around the edges of an image.
/// The border provides space for annotations or acts as a boundary when using advanced filtering techniques.
/// \tparam SrcView Models ImageViewConcept
/// \tparam extend_count number of rows/column to be added each side
/// \tparam option - TODO
template <typename SrcView>
auto extend_boundary(
SrcView src_view,
std::size_t extend_count,
boundary_option option
) -> typename gil::image<typename SrcView::value_type>
{
auto auxilary_img = extend_col(src_view, extend_count, option);
return extend_row(view(auxilary_img), extend_count, option);
}

}} // namespace boost::gil

#endif
3 changes: 2 additions & 1 deletion test/extension/numeric/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ foreach(_name
matrix3x2
numeric
pixel_numeric_operations
convolve_2d)
convolve_2d
extend_boundary)
set(_test t_ext_numeric_${_name})
set(_target test_ext_numeric_${_name})

Expand Down
208 changes: 208 additions & 0 deletions test/extension/numeric/extend_boundary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
//
// Copyright 2019 Pranam Lashkari <plashkari628@gmail.com>
//
// 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
//

#define BOOST_TEST_MODULE test_ext_numeric_extend_boundary
#include "unit_test.hpp"
#include "unit_test_utility.hpp"

#include <boost/gil.hpp>
#include <boost/gil/extension/numeric/algorithm.hpp>
#include <boost/gil/extension/numeric/convolve.hpp>

namespace gil = boost::gil;

std::uint8_t img[] =
{
1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 0, 0, 0, 0, 0, 0, 0, 32,
11, 0, 255, 0, 0, 0, 255, 0, 31,
12, 0, 0, 255, 0, 255, 0, 0, 30,
13, 0, 0, 0, 255, 0, 0, 0, 29,
14, 0, 0, 255, 0, 255, 0, 0, 28,
15, 0, 255, 0, 0, 0, 255, 0, 27,
16, 0, 0, 0, 0, 0, 0, 0, 26,
17, 18, 19, 20, 21, 22, 23, 24, 25
};

std::uint8_t row_output_constant[] =
{
1, 2, 3, 4, 5, 6, 7, 8, 9,
1, 2, 3, 4, 5, 6, 7, 8, 9,
1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 0, 0, 0, 0, 0, 0, 0, 32,
11, 0, 255, 0, 0, 0, 255, 0, 31,
12, 0, 0, 255, 0, 255, 0, 0, 30,
13, 0, 0, 0, 255, 0, 0, 0, 29,
14, 0, 0, 255, 0, 255, 0, 0, 28,
15, 0, 255, 0, 0, 0, 255, 0, 27,
16, 0, 0, 0, 0, 0, 0, 0, 26,
17, 18, 19, 20, 21, 22, 23, 24, 25,
17, 18, 19, 20, 21, 22, 23, 24, 25,
17, 18, 19, 20, 21, 22, 23, 24, 25
};

std::uint8_t row_output_zero[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 0, 0, 0, 0, 0, 0, 0, 32,
11, 0, 255, 0, 0, 0, 255, 0, 31,
12, 0, 0, 255, 0, 255, 0, 0, 30,
13, 0, 0, 0, 255, 0, 0, 0, 29,
14, 0, 0, 255, 0, 255, 0, 0, 28,
15, 0, 255, 0, 0, 0, 255, 0, 27,
16, 0, 0, 0, 0, 0, 0, 0, 26,
17, 18, 19, 20, 21, 22, 23, 24, 25,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
};

std::uint8_t col_output_constant[] =
{
1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9,
10, 10, 0, 0, 0, 0, 0, 0, 0, 32, 32,
11, 11, 0, 255, 0, 0, 0, 255, 0, 31, 31,
12, 12, 0, 0, 255, 0, 255, 0, 0, 30, 30,
13, 13, 0, 0, 0, 255, 0, 0, 0, 29, 29,
14, 14, 0, 0, 255, 0, 255, 0, 0, 28, 28,
15, 15, 0, 255, 0, 0, 0, 255, 0, 27, 27,
16, 16, 0, 0, 0, 0, 0, 0, 0, 26, 26,
17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25
};

std::uint8_t col_output_zero[] =
{
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0,
0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0,
0, 0, 11, 0, 255, 0, 0, 0, 255, 0, 31, 0, 0,
0, 0, 12, 0, 0, 255, 0, 255, 0, 0, 30, 0, 0,
0, 0, 13, 0, 0, 0, 255, 0, 0, 0, 29, 0, 0,
0, 0, 14, 0, 0, 255, 0, 255, 0, 0, 28, 0, 0,
0, 0, 15, 0, 255, 0, 0, 0, 255, 0, 27, 0, 0,
0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0,
0, 0, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0
};

std::uint8_t boundary_output_constant[] =
{
1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9,
1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9,
1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9,
10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 32, 32, 32,
11, 11, 11, 0, 255, 0, 0, 0, 255, 0, 31, 31, 31,
12, 12, 12, 0, 0, 255, 0, 255, 0, 0, 30, 30, 30,
13, 13, 13, 0, 0, 0, 255, 0, 0, 0, 29, 29, 29,
14, 14, 14, 0, 0, 255, 0, 255, 0, 0, 28, 28, 28,
15, 15, 15, 0, 255, 0, 0, 0, 255, 0, 27, 27, 27,
16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26,
17, 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25,
17, 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25,
17, 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25
};

std::uint8_t boundary_output_zero[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0,
0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0,
0, 0, 11, 0, 255, 0, 0, 0, 255, 0, 31, 0, 0,
0, 0, 12, 0, 0, 255, 0, 255, 0, 0, 30, 0, 0,
0, 0, 13, 0, 0, 0, 255, 0, 0, 0, 29, 0, 0,
0, 0, 14, 0, 0, 255, 0, 255, 0, 0, 28, 0, 0,
0, 0, 15, 0, 255, 0, 0, 0, 255, 0, 27, 0, 0,
0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0,
0, 0, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

BOOST_AUTO_TEST_SUITE(boundary_extension)

BOOST_AUTO_TEST_CASE(extend_row_with_constant)
{
gil::gray8c_view_t src_view =
gil::interleaved_view(9, 9, reinterpret_cast<const gil::gray8_pixel_t *>(img), 9);

gil::gray8c_view_t out_view =
gil::interleaved_view(9, 13, reinterpret_cast<const gil::gray8_pixel_t *>(row_output_constant), 9);

auto output = gil::extend_row(src_view, 2, gil::boundary_option::boundary_option_extend_constant);

BOOST_TEST(gil::equal_pixels(out_view, gil::view(output)));
}

BOOST_AUTO_TEST_CASE(extend_row_with_zero)
{
gil::gray8c_view_t src_view =
gil::interleaved_view(9, 9, reinterpret_cast<const gil::gray8_pixel_t *>(img), 9);

gil::gray8c_view_t out_view =
gil::interleaved_view(9, 15, reinterpret_cast<const gil::gray8_pixel_t *>(row_output_zero), 9);

auto output = gil::extend_row(src_view, 3, gil::boundary_option::boundary_option_extend_zero);

BOOST_TEST(gil::equal_pixels(out_view, gil::view(output)));
}

BOOST_AUTO_TEST_CASE(extend_col_with_constant)
{
gil::gray8c_view_t src_view =
gil::interleaved_view(9, 9, reinterpret_cast<const gil::gray8_pixel_t *>(img), 9);

gil::gray8c_view_t out_view =
gil::interleaved_view(11, 9, reinterpret_cast<const gil::gray8_pixel_t *>(col_output_constant), 11);

auto output = gil::extend_col(src_view, 1, gil::boundary_option::boundary_option_extend_constant);

BOOST_TEST(gil::equal_pixels(out_view, gil::view(output)));
}

BOOST_AUTO_TEST_CASE(extend_col_with_zero)
{
gil::gray8c_view_t src_view =
gil::interleaved_view(9, 9, reinterpret_cast<const gil::gray8_pixel_t *>(img), 9);

gil::gray8c_view_t out_view =
gil::interleaved_view(13, 9, reinterpret_cast<const gil::gray8_pixel_t *>(col_output_zero), 13);

auto output = gil::extend_col(src_view, 2, gil::boundary_option::boundary_option_extend_zero);

BOOST_TEST(gil::equal_pixels(out_view, gil::view(output)));
}

BOOST_AUTO_TEST_CASE(extend_img_with_constant)
{
gil::gray8c_view_t src_view =
gil::interleaved_view(9, 9, reinterpret_cast<const gil::gray8_pixel_t *>(img), 9);

gil::gray8c_view_t out_view =
gil::interleaved_view(13, 13, reinterpret_cast<const gil::gray8_pixel_t *>(boundary_output_constant), 13);

auto output = gil::extend_boundary(src_view, 2, gil::boundary_option::boundary_option_extend_constant);

BOOST_TEST(gil::equal_pixels(out_view, gil::view(output)));
}

BOOST_AUTO_TEST_CASE(extend_img_with_zero)
{
gil::gray8c_view_t src_view =
gil::interleaved_view(9, 9, reinterpret_cast<const gil::gray8_pixel_t *>(img), 9);

gil::gray8c_view_t out_view =
gil::interleaved_view(13, 13, reinterpret_cast<const gil::gray8_pixel_t *>(boundary_output_zero), 13);

auto output = gil::extend_boundary(src_view, 2, gil::boundary_option::boundary_option_extend_zero);

BOOST_TEST(gil::equal_pixels(out_view, gil::view(output)));
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit c46f31e

Please sign in to comment.