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:
@@ -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
139
example/morphology.cpp
Normal 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{});
|
||||
}
|
||||
}
|
||||
BIN
example/morphology_original.png
Normal file
BIN
example/morphology_original.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -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>
|
||||
|
||||
300
include/boost/gil/image_processing/morphology.hpp
Normal file
300
include/boost/gil/image_processing/morphology.hpp
Normal 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
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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 ;
|
||||
|
||||
136
test/core/image_processing/morphology.cpp
Normal file
136
test/core/image_processing/morphology.cpp
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user