Skip to content

Commit

Permalink
Added 2D convolution definitions to numeric extension
Browse files Browse the repository at this point in the history
2D convolution tests added

`convolve` function renamed to `convolve_1d`

closes #356
  • Loading branch information
miralshah365 committed Aug 7, 2019
1 parent bd12f3c commit abcd4f0
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 8 deletions.
66 changes: 65 additions & 1 deletion include/boost/gil/extension/numeric/convolve.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <boost/gil/algorithm.hpp>
#include <boost/gil/image_view_factory.hpp>
#include <boost/gil/metafunctions.hpp>
#include <boost/gil/utilities.hpp>

#include <boost/assert.hpp>

Expand Down Expand Up @@ -274,7 +275,7 @@ void convolve_cols(
/// \tparam DstView Models MutableImageViewConcept
template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
BOOST_FORCEINLINE
void convolve(
void convolve_1d(
SrcView const& src_view,
Kernel const& kernel,
DstView const& dst_view,
Expand Down Expand Up @@ -355,6 +356,69 @@ void convolve_cols_fixed(
transposed_view(src_view), kernel, transposed_view(dst_view), option);
}

namespace detail
{

template <typename SrcView, typename DstView, typename Kernel>
void convolve_2d_imp(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel)
{
int flip_ker_row, flip_ker_col, row_boundary, col_boundary;
float aux_total;
for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row) // rows
{
for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col) // columns
{
aux_total = 0.0f;
for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row) // kernel rows
{
flip_ker_row = kernel.size() - 1 - kernel_row; // row index of flipped kernel

for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col) // kernel columns
{
flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel

// index of input signal, used for checking boundary
row_boundary = view_row + (kernel.center_vertical() - flip_ker_row);
col_boundary = view_col + (kernel.center_horizontal() - flip_ker_col);

// ignore input samples which are out of bound
if (row_boundary >= 0 && row_boundary < src_view.height() &&
col_boundary >= 0 && col_boundary < src_view.width())
{
aux_total +=
src_view(col_boundary, row_boundary) *
kernel.at(flip_ker_row, flip_ker_col);
}
}
}
dst_view(view_col, view_row) = iround(aux_total);
}
}
}

} //namespace detail

template <typename SrcView, typename DstView, typename Kernel>
void convolve_2d(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel)
{
BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
BOOST_ASSERT(kernel.size() != 0);

gil_function_requires<ImageViewConcept<SrcView>>();
gil_function_requires<MutableImageViewConcept<DstView>>();
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");

for (std::size_t i = 0; i < src_view.num_channels(); i++)
{
detail::convolve_2d_imp
(nth_channel_view(src_view, i), nth_channel_view(dst_view, i), kernel);
}
}

}} // namespace boost::gil

#endif
2 changes: 1 addition & 1 deletion include/boost/gil/image_processing/threshold.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ void threshold_adaptive
typename image<typename SrcView::value_type>::view_t temp_view = view(temp_img);
SrcView temp_conv(temp_view);

convolve<pixel<float, typename SrcView::value_type::layout_t>>(
convolve_1d<pixel<float, typename SrcView::value_type::layout_t>>(
src_view, kernel, temp_view
);

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

Expand Down
1 change: 1 addition & 0 deletions test/extension/numeric/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ run kernel.cpp ;
compile-fail kernel_1d_fixed_even_size_fail.cpp ;
run matrix3x2.cpp ;
run numeric.cpp ;
convolve_2d.cpp ;
10 changes: 5 additions & 5 deletions test/extension/numeric/convolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
namespace gil = boost::gil;
namespace fixture = boost::gil::test::fixture;

BOOST_AUTO_TEST_SUITE(convolve)
BOOST_AUTO_TEST_SUITE(convolve_1d)

BOOST_AUTO_TEST_CASE_TEMPLATE(image_1x1_kernel_1x1_identity, Image, fixture::image_types)
{
Expand All @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(image_1x1_kernel_1x1_identity, Image, fixture::ima
using pixel_t = typename Image::value_type;
using channel_t = typename gil::channel_type<pixel_t>::type;
auto const kernel = fixture::create_kernel<channel_t>({1});
gil::convolve<pixel_t>(const_view(img_out), kernel, view(img_out));
gil::convolve_1d<pixel_t>(const_view(img_out), kernel, view(img_out));

// 1x1 kernel reduces convolution to multiplication
BOOST_TEST(gil::const_view(img).front() == gil::const_view(img_out).front());
Expand All @@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(image_1x1_kernel_3x3_identity, Image, fixture::ima
using pixel_t = typename Image::value_type;
using channel_t = typename gil::channel_type<pixel_t>::type;
auto const kernel = fixture::create_kernel<channel_t>({0, 0, 0, 0, 1, 0, 0, 0, 0});
gil::convolve<pixel_t>(const_view(img_out), kernel, view(img_out));
gil::convolve_1d<pixel_t>(const_view(img_out), kernel, view(img_out));

BOOST_TEST(gil::const_view(img).front() == gil::const_view(img_out).front());
}
Expand All @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(image_3x3_kernel_3x3_identity, Image, fixture::ima
Image img_out(img);

auto const kernel = fixture::create_kernel<channel_t>({0, 0, 0, 0, 1, 0, 0, 0, 0});
gil::convolve<pixel_t>(const_view(img_out), kernel, view(img_out));
gil::convolve_1d<pixel_t>(const_view(img_out), kernel, view(img_out));

BOOST_TEST(gil::equal_pixels(gil::const_view(img), gil::const_view(img_out)));
}
Expand All @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(image_5x5_kernel_3x3_identity, Image, fixture::ima
Image img_out(img);

auto const kernel = fixture::create_kernel<channel_t>({0, 0, 0, 0, 1, 0, 0, 0, 0});
gil::convolve<pixel_t>(const_view(img_out), kernel, view(img_out));
gil::convolve_1d<pixel_t>(const_view(img_out), kernel, view(img_out));
// TODO: Test different boundary options

BOOST_TEST(gil::equal_pixels(gil::const_view(img), gil::const_view(img_out)));
Expand Down
59 changes: 59 additions & 0 deletions test/extension/numeric/convolve_2d.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// Copyright 2019 Miral Shah <miralshah2211@gmail.com>
//
// Use, modification and distribution are subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

#include <cstddef>

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

#define BOOST_TEST_MODULE test_ext_convolve_2d
#include "unit_test.hpp"

namespace gil = boost::gil;

std::uint8_t img[] = {0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 255, 0, 0, 0, 255, 0, 0,
0, 0, 0, 255, 0, 255, 0, 0, 0,
0, 0, 0, 0, 255, 0, 0, 0, 0,
0, 0, 0, 255, 0, 255, 0, 0, 0,
0, 0, 255, 0, 0, 0, 255, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0};

std::uint8_t output[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 28, 28, 28, 0, 28, 28, 28, 0,
0, 28, 57, 57, 57, 57, 57, 28, 0,
0, 28, 57, 85, 85, 85, 57, 28, 0,
0, 0, 57, 85, 142, 85, 57, 0, 0,
0, 28, 57, 85, 85, 85, 57, 28, 0,
0, 28, 57, 57, 57, 57, 57, 28, 0,
0, 28, 28, 28, 0, 28, 28, 28, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0 };

BOOST_AUTO_TEST_SUITE(convolve_1d)

BOOST_AUTO_TEST_CASE(convolve_2d_test)
{
gil::gray8c_view_t src_view = gil::interleaved_view(9, 9, (const gil::gray8_pixel_t*)img, 9);

gil::image<gil::gray8_pixel_t> temp_img(src_view.width(), src_view.height());
typename gil::image<gil::gray8_pixel_t>::view_t temp_view = view(temp_img);
gil::gray8_view_t dst_view(temp_view);

std::vector<float> v(9, 1.0f / 9.0f);
gil::kernel_2d<float> kernel(v.begin(), v.size(), 1, 1);

gil::convolve_2d(src_view, dst_view, kernel);

gil::gray8c_view_t out_view = gil::interleaved_view(9, 9, (const gil::gray8_pixel_t*)output, 9);

BOOST_TEST(gil::equal_pixels(out_view, dst_view));
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit abcd4f0

Please sign in to comment.