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 Morphological Dilation and Erosion Algorithms #430

Closed
wants to merge 8 commits into from
47 changes: 47 additions & 0 deletions example/morphology.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright 2019 Ayush Bansal <[email protected]>
//
// 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/png.hpp>
ayushbansal07 marked this conversation as resolved.
Show resolved Hide resolved
#include <boost/gil/image_processing/morphology.hpp>
#include <boost/gil/image_view_factory.hpp>
#include <boost/gil.hpp>
#include <boost/multi_array.hpp>

const int struct_elem_size = 3;

using namespace boost::gil;
using namespace std;

int main()
{
gray8_image_t img;
read_image("test_adaptive.png", img, png_tag{});
gray8_image_t img_dilate(img.dimensions());
gray8_image_t img_erode(img.dimensions());

int se_arr[struct_elem_size][struct_elem_size] = {{0,1,0},{1,1,1},{0,1,0}};

using se_type = boost::multi_array<int, 2>;

se_type se{boost::extents[struct_elem_size][struct_elem_size]};
for (int i=0; i < struct_elem_size; ++i)
{
for (int j=0; j < struct_elem_size; ++j)
{
se[i][j] = se_arr[i][j];
}
}
auto img_view = const_view(img);
auto img_erode_view = view(img_erode);
auto img_dilate_view = view(img_dilate);

dilate(img_view, img_dilate_view, se);
erode(img_view, img_erode_view, se);

write_view("dilated_image.png", view(img_dilate), png_tag{});
write_view("eroded_image.png", view(img_erode), png_tag{});
}
135 changes: 135 additions & 0 deletions include/boost/gil/image_processing/morphology.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//
// Copyright 2019 Ayush Bansal <[email protected]>
//
// 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_MORPHOLOGY_HPP
#define BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP

#include <boost/gil/concepts.hpp>
#include <boost/gil/image.hpp>
#include <boost/assert.hpp>

#include <cstddef>

namespace boost { namespace gil {

// SE refers to Structuring Element
// Result type after convolution with the structuring element
enum class morphology_operator
{
erode,
dilate
};

namespace detail {

template <typename SrcView, typename DstView, typename StructElement>
void convolve_with_struct_element(SrcView const &src_view, DstView &dst_view,
StructElement const &struct_elem, std::ptrdiff_t src_y, std::ptrdiff_t src_x,
morphology_operator const &op)
{
// Template argument validation
gil_function_requires<ImageViewConcept<SrcView>>();
gil_function_requires<MutableImageViewConcept<DstView>>();

using source_channel_t = typename channel_type<SrcView>::type;

// Initial min and max values to be replaced after apply morphplogical operator
auto min = (std::numeric_limits<source_channel_t>::max());
auto max = (std::numeric_limits<source_channel_t>::min());

std::ptrdiff_t struct_elem_height = struct_elem.size();
std::ptrdiff_t struct_elem_width = struct_elem[0].size();

for (std::ptrdiff_t struct_elem_y = 0; struct_elem_y < struct_elem_height; ++struct_elem_y)
{
// The cooresponding value to be read from src_view is calculated as
// (src_y + struct_elem_y - struct_elem_height/2, src_x + struct_elem_x - struct_elem_width/2)
// NOTE: This calculation is only valid when the struct_elem dimensions are odd.
auto y_to_read = src_y + struct_elem_y - struct_elem_height/2;
if (y_to_read < 0 || y_to_read >= src_view.height())
continue;
typename SrcView::x_iterator src_it = src_view.row_begin(y_to_read);

for (std::ptrdiff_t struct_elem_x = 0; struct_elem_x < struct_elem_width; ++struct_elem_x)
{
// We need to consider only those positions which have 1 in Structuring Element
if (struct_elem[struct_elem_x][struct_elem_y] == 1)
{
auto x_to_read = src_x + struct_elem_x - struct_elem_width/2;
if (x_to_read < 0 || x_to_read >= src_view.width())
continue;
auto src_value = src_it[x_to_read];
if (src_value < min)
min = src_value;
if (src_value > max)
max = src_value;
}
}
}

typename DstView::x_iterator dst_it = dst_view.row_begin(src_y);

if (op == morphology_operator::erode)
dst_it[src_x] = min;
else if (op == morphology_operator::dilate)
dst_it[src_x] = max;
}

template <typename SrcView, typename DstView, typename StructElement>
void morph_impl(SrcView const &src_view, DstView &dst_view, StructElement const &struct_elem, morphology_operator const &op)
{
BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
BOOST_ASSERT(struct_elem.size() != 0 && struct_elem[0].size() != 0);

std::ptrdiff_t height = src_view.height();
std::ptrdiff_t width = dst_view.width();

for (std::ptrdiff_t src_y = 0; src_y < height; ++src_y)
{
for (std::ptrdiff_t src_x = 0; src_x < width; ++src_x)
{
convolve_with_struct_element<SrcView, DstView, StructElement>(src_view, dst_view, struct_elem, src_y, src_x, op);
}
}
}

} //namespace boost::gil::detail

/// \addtogroup ImageProcessing
/// @{

/// \brief Performs Morphological Dilation of the given image with a given Structuring Element.
/// \param src_view - The source image view.
/// \param dst_view - The destination image view (after performing dilation).
/// \param struct_elem - The Structuing Element. Should be a 2D-array type consiting of binary int values (0 and 1).
/// \tparam SrcView type of source image. Models gil::ImgaeViewConcept.
/// \tparam DstView type of destination image. Models gil::MutableImageViewConcept.
/// \tparam StructElement type of structuring element. Should provide size() and operator[].
mloskot marked this conversation as resolved.
Show resolved Hide resolved
template <typename SrcView, typename DstView, typename StructElement>
void dilate(SrcView const &src_view, DstView &dst_view, StructElement const &struct_elem)
{
morph_impl(src_view, dst_view, struct_elem, morphology_operator::dilate);
}

/// \brief Performs Morphological Erosion of the given image with a given Structuring Element.
/// \param src_view - The source image view.
/// \param dst_view - The destination image view (after performing dilation).
/// \param struct_elem - The Structuing Element. Should be a 2D-array type consiting of binary int values (0 and 1).
/// \tparam SrcView type of source image. Models gil::ImgaeViewConcept.
/// \tparam DstView type of destination image. Models gil::MutableImageViewConcept.
/// \tparam StructElement type of structuring element. Should provide size() and operator[].
template <typename SrcView, typename DstView, typename StructElement>
void erode(SrcView const &src_view, DstView &dst_view, StructElement const &struct_elem)
{
morph_impl(src_view, dst_view, struct_elem, morphology_operator::erode);
}

/// @}

}} //namespace boost::gil

#endif //BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP
1 change: 1 addition & 0 deletions test/core/image_processing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# http://www.boost.org/LICENSE_1_0.txt)
#
foreach(_name
morphology
threshold_binary
threshold_truncate
threshold_otsu)
Expand Down
Loading