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

Adding Gaussian Smoothening for images #443

Closed
wants to merge 15 commits into from
Closed
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
26 changes: 26 additions & 0 deletions example/gaussian_blur.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Copyright 2020 Laxmikant Suryavanshi <laxmikantsuryavanshi@hotmail.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 <boost/gil/extension/io/jpeg.hpp>
laxsuryavanshi marked this conversation as resolved.
Show resolved Hide resolved
#include <boost/gil/image_processing/filter.hpp>

using namespace boost::gil;

int main()
{
rgb8_image_t img;
read_image("test.jpg",img, jpeg_tag{});
rgb8_image_t img_out(img.dimensions());

// performing Gaussian Blur on image
// here kernel size is 5 and sigma is taken as 1
boost::gil::gaussian_filter(const_view(img), view(img_out), 5, 1.0f);
write_view("gaussian_blur.jpg", view(img_out), jpeg_tag{});

return 0;
}
57 changes: 56 additions & 1 deletion include/boost/gil/image_processing/filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
#include <boost/gil/image.hpp>
#include <boost/gil/image_view.hpp>

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <utility>
#include <vector>




namespace boost { namespace gil {

template <typename SrcView, typename DstView>
Expand Down Expand Up @@ -135,6 +137,59 @@ void median_filter(SrcView const& src_view, DstView const& dst_view, std::size_t
}
}

namespace detail {

template<typename KernelT>
void get_1d_gaussian_kernel(
KernelT& kernel,
long int kernel_size,
double sigma)
{
if ((kernel_size%2) == 0)
throw std::invalid_argument("kernel dimensions should be odd");

const double exp_denom = 2 * sigma * sigma;
auto center = kernel_size / 2;
for (long int x = 0; x <= center; x++)
{
const auto delta_x = center - x;
const double power = (delta_x * delta_x) / exp_denom;
const double numerator = std::exp(-power);
const float value = static_cast<float>(numerator/std::sqrt(M_PI * exp_denom));
kernel[x] = value;
kernel[kernel_size-1-x] = value;
}
}

} // namespace detail

template <typename SrcView, typename DstView>
void gaussian_filter(
SrcView src_view,
DstView dst_view,
long int kernel_size,
double sigma,
boundary_option option = boundary_option::extend_zero)
{
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");

std::vector<float> kernel_values(kernel_size);
detail::getGaussianKernel(kernel_values, kernel_size, sigma, normalize);
auto center = static_cast<int>(kernel_size/2);
kernel_1d<float> kernel(kernel_values.begin(), kernel_size, center);

detail::convolve_1d
<
pixel<float, typename SrcView::value_type::layout_t>
>(src_view, kernel, dst_view, option);
}

}} //namespace boost::gil

#endif // !BOOST_GIL_IMAGE_PROCESSING_FILTER_HPP
4 changes: 2 additions & 2 deletions include/boost/gil/image_processing/threshold.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void threshold_impl(SrcView const& src_view, DstView const& dst_view, Operator c
typename color_space_type<DstView>::type
>::value, "Source and destination views must have pixels with the same color space");

//iterate over the image chaecking each pixel value for the threshold
//iterate over the image checking each pixel value for the threshold
mloskot marked this conversation as resolved.
Show resolved Hide resolved
for (std::ptrdiff_t y = 0; y < src_view.height(); y++)
{
typename SrcView::x_iterator src_it = src_view.row_begin(y);
Expand All @@ -64,7 +64,7 @@ void threshold_impl(SrcView const& src_view, DstView const& dst_view, Operator c
/// @{
///
/// \brief Direction of image segmentation.
/// The direction specifieds which pixels are considered as corresponding to object
/// The direction specifies which pixels are considered as corresponding to object
mloskot marked this conversation as resolved.
Show resolved Hide resolved
/// and which pixels correspond to background.
enum class threshold_direction
{
Expand Down
62 changes: 62 additions & 0 deletions test/core/image_processing/gaussian_filter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// Copyright 2020 Laxmikant Suryavanshi <laxmikantsuryavanshi@hotmail.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)
//

#define BOOST_TEST_MODULE gil/test/core/image_processing/gaussian_filter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Licence and copyright notice is missing

#include "unit_test.hpp"

#include <boost/gil/algorithm.hpp>
#include <boost/gil/gray.hpp>
#include <boost/gil/image_view.hpp>
#include <boost/gil/image_processing/filter.hpp>

namespace gil = boost::gil;

const float kernel[] =
{
0.241971, 0.398942, 0.241971
};

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

std::uint8 output[] =
{
5, 15, 21, 15, 5,
15, 41, 56, 41, 15,
21, 56, 77, 56, 21,
15, 41, 56, 41, 15,
5, 15, 21, 15, 5
};

BOOST_AUTO_TEST_SUITE(filter)

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

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);

gil::gaussian_filter(src_view, dst_view, 3, 1.0f);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of your this implementation, even Gaussian kernel generation requires additional test but as I have suggested the changes above it would not be required.

Copy link
Contributor Author

@laxsuryavanshi laxsuryavanshi Mar 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified output of the Gaussian Kernel Generation code with cv's getGaussianKernel function and I find it very much same up to some extent (precision after 4 decimals)


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


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

BOOST_AUTO_TEST_SUITE_END()