2
0
mirror of https://github.com/boostorg/gil.git synced 2026-01-19 04:12:11 +00:00

Added all standard morphological transformations (#541)

* Added all standard morphological transformations

* Improved comments and some other things

* Applied adviced changes

* Applied adviced changes

* Should handle grayscale dilation/erosion

* Checking

* Added test cases and improved code structure

* Added command line control

* Added command line control

* Rectified some things

* Rectified some more things

* Improved comments

* Improved comments

* Improved doxygen comments and added more test cases

* Improved compatibility for builds and rectifying whitespace use

* Minor improvement in comments

* Did clang formatting

* pushed enum class inside namespace 'detail' and some other things

* Should handle multichannel images

* Clang formatting attempt

* got rid of if/else comparators for target_element

* Adds morphology.hpp declaration in boost/gil.hpp

* Fix newline

* (std::max)(a, b) instead of std::max(a, b)

* Improved Formatting
This commit is contained in:
meshtag
2021-02-13 21:31:22 +05:30
committed by GitHub
parent 2676d31801
commit 1e8526797e
8 changed files with 580 additions and 1 deletions

View File

@@ -30,6 +30,7 @@ local sources =
histogram.cpp
interleaved_ptr.cpp
mandelbrot.cpp
morphology.cpp
packed_pixel.cpp
resize.cpp
sobel_scharr.cpp

139
example/morphology.cpp Normal file
View File

@@ -0,0 +1,139 @@
//
// Copyright 2021 Prathamesh Tagore <prathameshtagore@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 <boost/gil.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <iostream>
#include <map>
#include <string>
#include <vector>
// Default structuring element is SE = [1,1,1]
// |1,1,1|
// [1,1,1]
// SE(1,1)(center pixel) is the one which coincides with the currently
// considered pixel of the image to be convolved. The structuring element can be
// easily changed by the user.
namespace gil = boost::gil;
int main(int argc, char** argv)
{
std::map<std::string, bool> operations;
if (argc < 4 || argc > 11)
{
throw std::invalid_argument(
"Wrong format of command line arguments.\n"
"Correct format is <Input_image.png> <Output_image_template>"
" <operation1> <operation2> <operation3> <operation4> <operation5> "
"<operation6>"
" <operation7> <operation8>\n");
// User has to enter atleast one operation and they can enter maximum 8
// operations considering binary conversion to be an
// operation.Output_image_template argument is the common component which
// will be added in names of all output images followed by a hyphen and
// the operation name.
// Example :
// ./example_morphology morphology_original.png out black_hat top_hat
// morphological_gradient dilation erosion opening closing binary
// Order of arguments entered will not matter with the exception of binary
// operation used for binary morphological operations.If binary is entered
// through the command line, it will always be the first operation to be
// applied.
return -1;
}
else
{
for (int i = 3; i < argc; ++i)
operations[argv[i]] = true;
}
gil::gray8_image_t img;
gil::read_image(argv[1], img, gil::png_tag{});
// Image can be converted to a binary format with high value as 255 and low
// value as 0 by using the threshold operator . This can be used for binary
// morphological operations . Convenient threshold for binary conversion may
// be chosen by the user.
if (operations["binary"])
{
threshold_binary(view(img), view(img), 170, 255);
std::string name = argv[2];
name += "-binary.png";
gil::write_view(name, view(img), gil::png_tag{});
}
std::vector<float> ker_vec(9, 1.0f); // Structuring element
gil::detail::kernel_2d<float> ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1);
gil::gray8_image_t img_out_dilation(img.dimensions()), img_out_erosion(img.dimensions()),
img_out_opening(img.dimensions());
gil::gray8_image_t img_out_closing(img.dimensions()), img_out_mg(img.dimensions()),
img_out_top_hat(img.dimensions());
gil::gray8_image_t img_out_black_hat(img.dimensions());
// Do not pass empty input image views in functions defined below for
// morphological operations to avoid errors.
if (operations["dilation"])
{
// dilate(input_image_view,output_image_view,structuring_element,iterations)
dilate(view(img), view(img_out_dilation), ker_mat, 1);
std::string name = argv[2];
name += "-dilation.png";
gil::write_view(name, view(img_out_dilation), gil::png_tag{});
}
if (operations["erosion"])
{
// erode(input_image_view,output_image_view,structuring_element,iterations)
erode(view(img), view(img_out_erosion), ker_mat, 1);
std::string name = argv[2];
name += "-erosion.png";
gil::write_view(name, view(img_out_erosion), gil::png_tag{});
}
if (operations["opening"])
{
// opening(input_image_view,output_image_view,structuring_element)
opening(view(img), view(img_out_opening), ker_mat);
std::string name = argv[2];
name += "-opening.png";
gil::write_view(name, view(img_out_opening), gil::png_tag{});
}
if (operations["closing"])
{
// closing(input_image_view,output_image_view,structuring_element)
closing(view(img), view(img_out_closing), ker_mat);
std::string name = argv[2];
name += "-closing.png";
gil::write_view(name, view(img_out_closing), gil::png_tag{});
}
if (operations["morphological_gradient"])
{
// morphological_gradient(input_image_view,output_image_view,structuring_element)
morphological_gradient(view(img), view(img_out_mg), ker_mat);
std::string name = argv[2];
name += "-morphological_gradient.png";
gil::write_view(name, view(img_out_mg), gil::png_tag{});
}
if (operations["top_hat"])
{
// top_hat(input_image_view,output_image_view,structuring_element)
top_hat(view(img), view(img_out_top_hat), ker_mat);
std::string name = argv[2];
name += "-top_hat.png";
gil::write_view(name, view(img_out_top_hat), gil::png_tag{});
}
if (operations["black_hat"])
{
// black_hat(input_image_view,output_image_view,structuring_element)
black_hat(view(img), view(img_out_black_hat), ker_mat);
std::string name = argv[2];
name += "-black_hat.png";
gil::write_view(name, view(img_out_black_hat), gil::png_tag{});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -57,6 +57,7 @@
#include <boost/gil/image_processing/histogram_matching.hpp>
#include <boost/gil/image_processing/hough_parameter.hpp>
#include <boost/gil/image_processing/hough_transform.hpp>
#include <boost/gil/image_processing/morphology.hpp>
#include <boost/gil/image_processing/numeric.hpp>
#include <boost/gil/image_processing/scaling.hpp>
#include <boost/gil/image_processing/threshold.hpp>

View File

@@ -0,0 +1,300 @@
//
// Copyright 2021 Prathamesh Tagore <prathameshtagore@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)
//
#ifndef BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP
#define BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP
#include <boost/gil/extension/numeric/kernel.hpp>
#include <boost/gil/gray.hpp>
#include <boost/gil/image_processing/threshold.hpp>
namespace boost
{
namespace gil
{
namespace detail
{
enum class morphological_operation
{
dilation,
erosion,
};
/// \addtogroup ImageProcessing
/// @{
/// \brief Implements morphological operations at pixel level.This function
/// compares neighbouring pixel values according to the kernel and choose
/// minimum/mamximum neighbouring pixel value and assigns it to the pixel under
/// consideration.
/// \param src_view - Source/Input image view.
/// \param dst_view - View which stores the final result of operations performed by this function.
/// \param kernel - Kernel matrix/structuring element containing 0's and 1's
/// which will be used for applying the required morphological operation.
/// \param identifier - Indicates the type of morphological operation to be applied.
/// \tparam SrcView type of source image.
/// \tparam DstView type of output image.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename DstView, typename Kernel>
void morph_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel,
morphological_operation identifier)
{
std::ptrdiff_t flip_ker_row, flip_ker_col, row_boundary, col_boundary;
typename channel_type<typename SrcView::value_type>::type target_element;
for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row)
{
for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col)
{
target_element = src_view(view_col, view_row);
for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row)
{
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)
{
flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel
// We ensure that we consider only those pixels which are overlapped
// on a non-zero kernel_element as
if (kernel.at(flip_ker_row, flip_ker_col) == 0)
{
continue;
}
// index of input signal, used for checking boundary
row_boundary = view_row + (kernel.center_y() - flip_ker_row);
col_boundary = view_col + (kernel.center_x() - 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())
{
if (identifier == morphological_operation::dilation)
{
target_element =
(std::max)(src_view(col_boundary, row_boundary)[0], target_element);
}
else if (identifier == morphological_operation::erosion)
{
target_element =
(std::min)(src_view(col_boundary, row_boundary)[0], target_element);
}
}
}
}
dst_view(view_col, view_row) = target_element;
}
}
}
/// \brief Checks feasibility of the desired operation and passes parameter
/// values to the function morph_impl alongwith individual channel views of the
/// input image.
/// \param src_view - Source/Input image view.
/// \param dst_view - View which stores the final result of operations performed by this function.
/// \param kernel - Kernel matrix/structuring element containing 0's and 1's
/// which will be used for applying the required morphological operation.
/// \param identifier - Indicates the type of morphological operation to be applied.
/// \tparam SrcView type of source image.
/// \tparam DstView type of output image.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename DstView, typename Kernel>
void morph(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat,
morphological_operation identifier)
{
BOOST_ASSERT(ker_mat.size() != 0 && src_view.dimensions() == dst_view.dimensions());
gil_function_requires<ImageViewConcept<SrcView>>();
gil_function_requires<MutableImageViewConcept<DstView>>();
gil_function_requires<ColorSpacesCompatibleConcept<typename color_space_type<SrcView>::type,
typename color_space_type<DstView>::type>>();
gil::image<typename DstView::value_type> intermediate_img(src_view.dimensions());
for (std::size_t i = 0; i < src_view.num_channels(); i++)
{
morph_impl(nth_channel_view(src_view, i), nth_channel_view(view(intermediate_img), i),
ker_mat, identifier);
}
copy_pixels(view(intermediate_img), dst_view);
}
/// \brief Calculates the difference between pixel values of first image_view
/// and second image_view.
/// \param src_view1 - First parameter for subtraction of views.
/// \param src_view2 - Second parameter for subtraction of views.
/// \param diff_view - View containing result of the subtraction of second view from
/// the first view.
/// \tparam SrcView type of source/Input images used for subtraction.
/// \tparam DiffView type of image view containing the result of subtraction.
template <typename SrcView, typename DiffView>
void difference_impl(SrcView const& src_view1, SrcView const& src_view2, DiffView const& diff_view)
{
for (std::ptrdiff_t view_row = 0; view_row < src_view1.height(); ++view_row)
for (std::ptrdiff_t view_col = 0; view_col < src_view1.width(); ++view_col)
diff_view(view_col, view_row) =
src_view1(view_col, view_row) - src_view2(view_col, view_row);
}
/// \brief Passes parameter values to the function 'difference_impl' alongwith
/// individual channel views of input images.
/// \param src_view1 - First parameter for subtraction of views.
/// \param src_view2 - Second parameter for subtraction of views.
/// \param diff_view - View containing result of the subtraction of second view from the first view.
/// \tparam SrcView type of source/Input images used for subtraction.
/// \tparam DiffView type of image view containing the result of subtraction.
template <typename SrcView, typename DiffView>
void difference(SrcView const& src_view1, SrcView const& src_view2, DiffView const& diff_view)
{
gil_function_requires<ImageViewConcept<SrcView>>();
gil_function_requires<MutableImageViewConcept<DiffView>>();
gil_function_requires<ColorSpacesCompatibleConcept<
typename color_space_type<SrcView>::type, typename color_space_type<DiffView>::type>>();
for (std::size_t i = 0; i < src_view1.num_channels(); i++)
{
difference_impl(nth_channel_view(src_view1, i), nth_channel_view(src_view2, i),
nth_channel_view(diff_view, i));
}
}
} // namespace detail
/// \brief Applies morphological dilation on the input image view using given
/// structuring element. It gives the maximum overlapped value to the pixel
/// overlapping with the center element of structuring element. \param src_view
/// - Source/input image view.
/// \param int_op_view - view for writing output and performing intermediate operations.
/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
/// applying dilation.
/// \param iterations - Specifies the number of times dilation is to be applied on the input image
/// view.
/// \tparam SrcView type of source image, models gil::ImageViewConcept.
/// \tparam IntOpView type of output image, models gil::MutableImageViewConcept.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename IntOpView, typename Kernel>
void dilate(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat,
int iterations)
{
copy_pixels(src_view, int_op_view);
for (int i = 0; i < iterations; ++i)
morph(int_op_view, int_op_view, ker_mat, detail::morphological_operation::dilation);
}
/// \brief Applies morphological erosion on the input image view using given
/// structuring element. It gives the minimum overlapped value to the pixel
/// overlapping with the center element of structuring element.
/// \param src_view - Source/input image view.
/// \param int_op_view - view for writing output and performing intermediate operations.
/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
/// applying erosion.
/// \param iterations - Specifies the number of times erosion is to be applied on the input
/// image view.
/// \tparam SrcView type of source image, models gil::ImageViewConcept.
/// \tparam IntOpView type of output image, models gil::MutableImageViewConcept.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename IntOpView, typename Kernel>
void erode(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat,
int iterations)
{
copy_pixels(src_view, int_op_view);
for (int i = 0; i < iterations; ++i)
morph(int_op_view, int_op_view, ker_mat, detail::morphological_operation::erosion);
}
/// \brief Performs erosion and then dilation on the input image view . This
/// operation is utilized for removing noise from images.
/// \param src_view - Source/input image view.
/// \param int_op_view - view for writing output and performing intermediate operations.
/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
/// applying the opening operation.
/// \tparam SrcView type of source image, models gil::ImageViewConcept.
/// \tparam IntOpView type of output image, models gil::MutableImageViewConcept.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename IntOpView, typename Kernel>
void opening(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat)
{
erode(src_view, int_op_view, ker_mat, 1);
dilate(int_op_view, int_op_view, ker_mat, 1);
}
/// \brief Performs dilation and then erosion on the input image view which is
/// exactly opposite to the opening operation . Closing operation can be
/// utilized for closing small holes inside foreground objects.
/// \param src_view - Source/input image view.
/// \param int_op_view - view for writing output and performing intermediate operations.
/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
/// applying the closing operation.
/// \tparam SrcView type of source image, models gil::ImageViewConcept.
/// \tparam IntOpView type of output image, models gil::MutableImageViewConcept.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename IntOpView, typename Kernel>
void closing(SrcView const& src_view, IntOpView const& int_op_view, Kernel const& ker_mat)
{
dilate(src_view, int_op_view, ker_mat, 1);
erode(int_op_view, int_op_view, ker_mat, 1);
}
/// \brief Calculates the difference between image views generated after
/// applying dilation dilation and erosion on an image . The resultant image
/// will look like the outline of the object(s) present in the image.
/// \param src_view - Source/input image view.
/// \param dst_view - Destination view which will store the final result of morphological
/// gradient operation.
/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which
/// will be used for applying the morphological gradient operation.
/// \tparam SrcView type of source image, models gil::ImageViewConcept.
/// \tparam DstView type of output image, models gil::MutableImageViewConcept.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename DstView, typename Kernel>
void morphological_gradient(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat)
{
using namespace boost::gil;
gil::image<typename DstView::value_type> int_dilate(src_view.dimensions()),
int_erode(src_view.dimensions());
dilate(src_view, view(int_dilate), ker_mat, 1);
erode(src_view, view(int_erode), ker_mat, 1);
difference(view(int_dilate), view(int_erode), dst_view);
}
/// \brief Calculates the difference between input image view and the view
/// generated by opening operation on the input image view.
/// \param src_view - Source/input image view.
/// \param dst_view - Destination view which will store the final result of top hat operation.
/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's which will be used for
/// applying the top hat operation.
/// \tparam SrcView type of source image, models gil::ImageViewConcept.
/// \tparam DstView type of output image, models gil::MutableImageViewConcept.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename DstView, typename Kernel>
void top_hat(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat)
{
using namespace boost::gil;
gil::image<typename DstView::value_type> int_opening(src_view.dimensions());
opening(src_view, view(int_opening), ker_mat);
difference(src_view, view(int_opening), dst_view);
}
/// \brief Calculates the difference between closing of the input image and
/// input image.
/// \param src_view - Source/input image view.
/// \param dst_view - Destination view which will store the final result of black hat operation.
/// \param ker_mat - Kernel matrix/structuring element containing 0's and 1's
/// which will be used for applying the black hat operation.
/// \tparam SrcView type of source image, models gil::ImageViewConcept.
/// \tparam DstView type of output image, models gil::MutableImageViewConcept.
/// \tparam Kernel type of structuring element.
template <typename SrcView, typename DstView, typename Kernel>
void black_hat(SrcView const& src_view, DstView const& dst_view, Kernel const& ker_mat)
{
using namespace boost::gil;
gil::image<typename DstView::value_type> int_closing(src_view.dimensions());
closing(src_view, view(int_closing), ker_mat);
difference(view(int_closing), src_view, dst_view);
}
/// @}
}} // namespace boost::gil
#endif // BOOST_GIL_IMAGE_PROCESSING_MORPHOLOGY_HPP

View File

@@ -9,7 +9,8 @@
foreach(_name
threshold_binary
threshold_truncate
threshold_otsu)
threshold_otsu
morphology)
set(_test t_core_image_processing_${_name})
set(_target test_core_image_processing_${_name})

View File

@@ -22,3 +22,4 @@ run median_filter.cpp ;
run anisotropic_diffusion.cpp ;
run hough_line_transform.cpp ;
run hough_circle_transform.cpp ;
run morphology.cpp ;

View File

@@ -0,0 +1,136 @@
//
// Copyright 2021 Prathamesh Tagore <prathameshtagore@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 <boost/core/lightweight_test.hpp>
#include <boost/gil/image_processing/morphology.hpp>
#include <vector>
namespace gil = boost::gil;
// This function helps us fill pixels of a view given as 2nd argument with
// elements of the vector given as 1st argument.
void pixel_fill(std::vector<std::vector<int>>& original_binary_vector,
boost::gil::gray8_image_t& original_img)
{
for (std::ptrdiff_t view_row = 0; view_row < view(original_img).height(); ++view_row)
{
for (std::ptrdiff_t view_col = 0; view_col < view(original_img).width(); ++view_col)
{
view(original_img)(view_col, view_row) =
gil::gray8_pixel_t(original_binary_vector[view_row][view_col]);
}
}
}
int main()
{
std::vector<std::vector<int>> original_binary_vector{
{0, 0, 0, 0, 0, 0}, {0, 0, 127, 144, 143, 0}, {0, 0, 128, 0, 142, 0},
{0, 0, 129, 0, 141, 0}, {0, 0, 130, 140, 139, 0}, {0, 0, 131, 0, 0, 0},
{0, 0, 132, 137, 136, 138}, {0, 0, 133, 134, 135, 0}};
std::vector<std::vector<int>> orig_dil_imp{
{255, 100, 100, 100}, {100, 100, 100, 100}, {100, 100, 100, 100}};
// All vectors defined below will be used for creating expected image views
// which are supposed to match the views obtained after applying morphological
// operations.
std::vector<std::vector<int>> exp_dil{
{0, 127, 144, 144, 144, 143}, {0, 128, 144, 144, 144, 143}, {0, 129, 144, 144, 144, 143},
{0, 130, 140, 142, 142, 142}, {0, 131, 140, 141, 141, 141}, {0, 132, 140, 140, 140, 139},
{0, 133, 137, 137, 138, 138}, {0, 133, 137, 137, 138, 138}};
// Following vector intends to check result of dilation operation when it is
// applied 2 times on the original image.
std::vector<std::vector<int>> exp_dil_iter2{
{128, 144, 144, 144, 144, 144}, {129, 144, 144, 144, 144, 144},
{130, 144, 144, 144, 144, 144}, {131, 144, 144, 144, 144, 144},
{132, 140, 142, 142, 142, 142}, {133, 140, 141, 141, 141, 141},
{133, 140, 140, 140, 140, 140}, {133, 137, 137, 138, 138, 138}};
std::vector<std::vector<int>> exp_er{
{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, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 132, 0, 0}};
// Following vector intends to check result of erosion operation when it is
// applied 2 times on the original image.
std::vector<std::vector<int>> exp_er_iter2{
{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, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}};
std::vector<std::vector<int>> exp_opening{
{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, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 132, 132, 132, 0}, {0, 0, 132, 132, 132, 0}};
std::vector<std::vector<int>> exp_closing{
{0, 0, 127, 144, 143, 143}, {0, 0, 127, 144, 143, 143}, {0, 0, 128, 140, 142, 142},
{0, 0, 129, 140, 141, 141}, {0, 0, 130, 140, 139, 139}, {0, 0, 131, 137, 137, 138},
{0, 0, 132, 137, 137, 138}, {0, 0, 133, 137, 137, 138}};
std::vector<std::vector<int>> exp_mg{{0, 127, 144, 144, 144, 143}, {0, 128, 144, 144, 144, 143},
{0, 129, 144, 144, 144, 143}, {0, 130, 140, 142, 142, 142},
{0, 131, 140, 141, 141, 141}, {0, 132, 140, 140, 140, 139},
{0, 133, 137, 137, 138, 138}, {0, 133, 137, 5, 138, 138}};
std::vector<std::vector<int>> exp_top_hat{{0, 0, 0, 0, 0, 0}, {0, 0, 127, 144, 143, 0},
{0, 0, 128, 0, 142, 0}, {0, 0, 129, 0, 141, 0},
{0, 0, 130, 140, 139, 0}, {0, 0, 131, 0, 0, 0},
{0, 0, 0, 5, 4, 138}, {0, 0, 1, 2, 3, 0}};
std::vector<std::vector<int>> exp_black_hat{
{0, 0, 127, 144, 143, 143}, {0, 0, 0, 0, 0, 143}, {0, 0, 0, 140, 0, 142},
{0, 0, 0, 140, 0, 141}, {0, 0, 0, 0, 0, 139}, {0, 0, 0, 137, 137, 138},
{0, 0, 0, 0, 1, 0}, {0, 0, 0, 3, 2, 138}};
std::vector<std::vector<int>> exp_dil_imp{
{255, 255, 100, 100}, {255, 255, 100, 100}, {100, 100, 100, 100}};
gil::gray8_image_t original_img(6, 8), obtained_dilation(6, 8), expected_dilation(6, 8);
gil::gray8_image_t obtained_erosion(6, 8), expected_erosion(6, 8);
gil::gray8_image_t obtained_opening(6, 8), expected_opening(6, 8);
gil::gray8_image_t obtained_closing(6, 8), expected_closing(6, 8);
gil::gray8_image_t obtained_mg(6, 8), expected_mg(6, 8);
gil::gray8_image_t obtained_top_hat(6, 8), expected_top_hat(6, 8);
gil::gray8_image_t obtained_black_hat(6, 8), expected_black_hat(6, 8);
gil::gray8_image_t obtained_dil_iter2(6, 8), expected_dil_iter2(6, 8);
gil::gray8_image_t obtained_er_iter2(6, 8), expected_er_iter2(6, 8);
gil::gray8_image_t obtained_imp_dil(4, 3), expected_imp_dil(4, 3), original_imp_dil(4, 3);
std::vector<float> ker_vec(9, 1.0f); // Structuring element
gil::detail::kernel_2d<float> ker_mat(ker_vec.begin(), ker_vec.size(), 1, 1);
pixel_fill(original_binary_vector, original_img);
pixel_fill(exp_dil, expected_dilation);
pixel_fill(exp_er, expected_erosion);
pixel_fill(exp_opening, expected_opening);
pixel_fill(exp_closing, expected_closing);
pixel_fill(exp_mg, expected_mg);
pixel_fill(exp_top_hat, expected_top_hat);
pixel_fill(exp_black_hat, expected_black_hat);
pixel_fill(exp_dil_iter2, expected_dil_iter2);
pixel_fill(orig_dil_imp, original_imp_dil);
pixel_fill(exp_dil_imp, expected_imp_dil);
pixel_fill(exp_er_iter2, expected_er_iter2);
// Different morphological operations are applied on the same initial image to
// obtain results of our implementation which are then compared with expected
// results.
gil::dilate(view(original_img), view(obtained_dilation), ker_mat, 1);
gil::erode(view(original_img), view(obtained_erosion), ker_mat, 1);
gil::opening(view(original_img), view(obtained_opening), ker_mat);
gil::closing(view(original_img), view(obtained_closing), ker_mat);
gil::morphological_gradient(view(original_img), view(obtained_mg), ker_mat);
gil::top_hat(view(original_img), view(obtained_top_hat), ker_mat);
gil::black_hat(view(original_img), view(obtained_black_hat), ker_mat);
gil::dilate(view(original_imp_dil), view(obtained_imp_dil), ker_mat, 1);
gil::dilate(view(original_img), view(obtained_dil_iter2), ker_mat, 2);
gil::erode(view(original_img), view(obtained_er_iter2), ker_mat, 2);
// Testing obtained results with expected results.
BOOST_TEST(gil::equal_pixels(gil::view(obtained_dilation), gil::view(expected_dilation)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_erosion), gil::view(expected_erosion)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_opening), gil::view(expected_opening)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_closing), gil::view(expected_closing)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_mg), gil::view(expected_mg)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_top_hat), gil::view(expected_top_hat)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_black_hat), gil::view(expected_black_hat)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_imp_dil), gil::view(expected_imp_dil)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_dil_iter2), gil::view(expected_dil_iter2)));
BOOST_TEST(gil::equal_pixels(gil::view(obtained_er_iter2), gil::view(expected_er_iter2)));
return boost::report_errors();
}